From e3fe30a94403217b1a015e459ae6ad896e633e79 Mon Sep 17 00:00:00 2001 From: Santosh Kulkarni <66682828+kr-santosh@users.noreply.github.com> Date: Tue, 4 Jan 2022 21:09:48 +0530 Subject: [PATCH] Client Encryption: Adds an abstraction layer to hide the underlying usage of Microsoft Data Encryption(MDE) library. (#2906) This PR adds in an abstraction layer to hide the underlying dependency on Microsoft Data Encryption library. The abstraction is limited to only the classes which are currently exposed to public methods of client side encryption SDK. --- .../src/EncryptionContainerExtensions.cs | 2 +- .../src/EncryptionCosmosClient.cs | 9 +- .../src/EncryptionCosmosClientExtensions.cs | 22 +- .../src/EncryptionDatabaseExtensions.cs | 28 +- .../src/EncryptionSettingForProperty.cs | 20 +- .../src/EncryptionSettings.cs | 8 +- .../AzureKeyVaultKeyWrapProvider.cs | 77 ++++++ .../MdeSupport/DataEncryptionKeyAlgorithm.cs | 18 ++ .../EncryptionKeyStoreProviderImpl.cs | 74 ++++++ .../MdeSupport/EncryptionKeyWrapProvider.cs | 92 +++++++ .../EncryptionType.cs} | 12 +- .../MdeSupport/KeyEncryptionKeyAlgorithm.cs | 17 ++ .../Microsoft.Azure.Cosmos.Encryption.csproj | 1 + .../src/QueryDefinitionExtensions.cs | 2 +- .../tests/EmulatorTests/MdeEncryptionTests.cs | 240 +++++++++++------- .../Contracts/DotNetSDKEncryptionAPI.json | 150 ++++++++++- 16 files changed, 627 insertions(+), 145 deletions(-) create mode 100644 Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/AzureKeyVaultKeyWrapProvider.cs create mode 100644 Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/DataEncryptionKeyAlgorithm.cs create mode 100644 Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionKeyStoreProviderImpl.cs create mode 100644 Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionKeyWrapProvider.cs rename Microsoft.Azure.Cosmos.Encryption/src/{CosmosEncryptionType.cs => MdeSupport/EncryptionType.cs} (58%) create mode 100644 Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/KeyEncryptionKeyAlgorithm.cs diff --git a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionContainerExtensions.cs b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionContainerExtensions.cs index b20758d1bc..c0852f9be5 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionContainerExtensions.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionContainerExtensions.cs @@ -29,7 +29,7 @@ public static class EncryptionContainerExtensions /// /// /// diff --git a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionCosmosClient.cs b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionCosmosClient.cs index 8779f61a93..89dfc4b7bd 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionCosmosClient.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionCosmosClient.cs @@ -8,25 +8,26 @@ namespace Microsoft.Azure.Cosmos.Encryption using System.Net; using System.Threading; using System.Threading.Tasks; - using Microsoft.Data.Encryption.Cryptography; /// /// CosmosClient with Encryption support. /// internal sealed class EncryptionCosmosClient : CosmosClient { + internal static readonly SemaphoreSlim EncryptionKeyCacheSemaphore = new SemaphoreSlim(1, 1); + private readonly CosmosClient cosmosClient; private readonly AsyncCache clientEncryptionKeyPropertiesCacheByKeyId; - public EncryptionCosmosClient(CosmosClient cosmosClient, EncryptionKeyStoreProvider encryptionKeyStoreProvider) + public EncryptionCosmosClient(CosmosClient cosmosClient, EncryptionKeyWrapProvider encryptionKeyWrapProvider) { this.cosmosClient = cosmosClient ?? throw new ArgumentNullException(nameof(cosmosClient)); - this.EncryptionKeyStoreProvider = encryptionKeyStoreProvider ?? throw new ArgumentNullException(nameof(encryptionKeyStoreProvider)); + this.EncryptionKeyWrapProvider = encryptionKeyWrapProvider ?? throw new ArgumentNullException(nameof(encryptionKeyWrapProvider)); this.clientEncryptionKeyPropertiesCacheByKeyId = new AsyncCache(); } - public EncryptionKeyStoreProvider EncryptionKeyStoreProvider { get; } + public EncryptionKeyWrapProvider EncryptionKeyWrapProvider { get; } public override CosmosClientOptions ClientOptions => this.cosmosClient.ClientOptions; diff --git a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionCosmosClientExtensions.cs b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionCosmosClientExtensions.cs index 2419e78676..ad2077f3dc 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionCosmosClientExtensions.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionCosmosClientExtensions.cs @@ -16,15 +16,15 @@ public static class EncryptionCosmosClientExtensions /// Get Cosmos Client with Encryption support for performing operations using client-side encryption. /// /// Regular Cosmos Client. - /// EncryptionKeyStoreProvider, provider that allows interaction with the master keys. + /// EncryptionKeyWrapProvider, provider that allows interaction with the master keys. /// CosmosClient to perform operations supporting client-side encryption / decryption. public static CosmosClient WithEncryption( this CosmosClient cosmosClient, - EncryptionKeyStoreProvider encryptionKeyStoreProvider) + EncryptionKeyWrapProvider encryptionKeyWrapProvider) { - if (encryptionKeyStoreProvider == null) + if (encryptionKeyWrapProvider == null) { - throw new ArgumentNullException(nameof(encryptionKeyStoreProvider)); + throw new ArgumentNullException(nameof(encryptionKeyWrapProvider)); } if (cosmosClient == null) @@ -32,19 +32,7 @@ public static CosmosClient WithEncryption( throw new ArgumentNullException(nameof(cosmosClient)); } - // set the TTL for ProtectedDataEncryption at the Encryption CosmosClient Init so that we have a uniform expiry of the KeyStoreProvider and ProtectedDataEncryption cache items. - if (encryptionKeyStoreProvider.DataEncryptionKeyCacheTimeToLive.HasValue) - { - ProtectedDataEncryptionKey.TimeToLive = encryptionKeyStoreProvider.DataEncryptionKeyCacheTimeToLive.Value; - } - else - { - // If null is passed to DataEncryptionKeyCacheTimeToLive it results in forever caching hence setting - // arbitrarily large caching period. ProtectedDataEncryptionKey does not seem to handle TimeSpan.MaxValue. - ProtectedDataEncryptionKey.TimeToLive = TimeSpan.FromDays(36500); - } - - return new EncryptionCosmosClient(cosmosClient, encryptionKeyStoreProvider); + return new EncryptionCosmosClient(cosmosClient, encryptionKeyWrapProvider); } } } diff --git a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionDatabaseExtensions.cs b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionDatabaseExtensions.cs index 1a323f6d89..417c9f3856 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionDatabaseExtensions.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionDatabaseExtensions.cs @@ -38,7 +38,7 @@ public static class EncryptionDatabaseExtensions public static async Task CreateClientEncryptionKeyAsync( this Database database, string clientEncryptionKeyId, - DataEncryptionKeyAlgorithm dataEncryptionKeyAlgorithm, + string dataEncryptionKeyAlgorithm, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata, CancellationToken cancellationToken = default) { @@ -49,11 +49,9 @@ public static async Task CreateClientEncryptionKeyA throw new ArgumentNullException(nameof(clientEncryptionKeyId)); } - string encryptionAlgorithm = dataEncryptionKeyAlgorithm.ToString(); - - if (!string.Equals(encryptionAlgorithm, DataEncryptionKeyAlgorithm.AEAD_AES_256_CBC_HMAC_SHA256.ToString())) + if (!string.Equals(dataEncryptionKeyAlgorithm, DataEncryptionKeyAlgorithm.AeadAes256CbcHmacSha256)) { - throw new ArgumentException($"Invalid Encryption Algorithm '{encryptionAlgorithm}' passed. Please refer to https://aka.ms/CosmosClientEncryption for more details. "); + throw new ArgumentException($"Invalid Encryption Algorithm '{dataEncryptionKeyAlgorithm}' passed. Please refer to https://aka.ms/CosmosClientEncryption for more details. "); } if (encryptionKeyWrapMetadata == null) @@ -65,17 +63,17 @@ public static async Task CreateClientEncryptionKeyA ? encryptionDatabase.EncryptionCosmosClient : throw new ArgumentException("Creating a ClientEncryptionKey resource requires the use of an encryption - enabled client. Please refer to https://aka.ms/CosmosClientEncryption for more details. "); - EncryptionKeyStoreProvider encryptionKeyStoreProvider = encryptionCosmosClient.EncryptionKeyStoreProvider; + EncryptionKeyWrapProvider encryptionKeyWrapProvider = encryptionCosmosClient.EncryptionKeyWrapProvider; - if (!string.Equals(encryptionKeyWrapMetadata.Type, encryptionKeyStoreProvider.ProviderName)) + if (!string.Equals(encryptionKeyWrapMetadata.Type, encryptionKeyWrapProvider.ProviderName)) { - throw new ArgumentException("The EncryptionKeyWrapMetadata Type value does not match with the ProviderName of EncryptionKeyStoreProvider configured on the Client. Please refer to https://aka.ms/CosmosClientEncryption for more details. "); + throw new ArgumentException("The EncryptionKeyWrapMetadata Type value does not match with the ProviderName of EncryptionKeyWrapProvider configured on the Client. Please refer to https://aka.ms/CosmosClientEncryption for more details. "); } KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate( encryptionKeyWrapMetadata.Name, encryptionKeyWrapMetadata.Value, - encryptionKeyStoreProvider); + encryptionKeyWrapProvider.EncryptionKeyStoreProviderImpl); ProtectedDataEncryptionKey protectedDataEncryptionKey = new ProtectedDataEncryptionKey( clientEncryptionKeyId, @@ -91,7 +89,7 @@ public static async Task CreateClientEncryptionKeyA ClientEncryptionKeyProperties clientEncryptionKeyProperties = new ClientEncryptionKeyProperties( clientEncryptionKeyId, - encryptionAlgorithm, + dataEncryptionKeyAlgorithm, wrappedDataEncryptionKey, encryptionKeyWrapMetadata); @@ -145,11 +143,11 @@ public static async Task RewrapClientEncryptionKeyA ? encryptionDatabase.EncryptionCosmosClient : throw new ArgumentException("Rewraping a ClientEncryptionKey requires the use of an encryption - enabled client. Please refer to https://aka.ms/CosmosClientEncryption for more details. "); - EncryptionKeyStoreProvider encryptionKeyStoreProvider = encryptionCosmosClient.EncryptionKeyStoreProvider; + EncryptionKeyWrapProvider encryptionKeyWrapProvider = encryptionCosmosClient.EncryptionKeyWrapProvider; - if (!string.Equals(newEncryptionKeyWrapMetadata.Type, encryptionKeyStoreProvider.ProviderName)) + if (!string.Equals(newEncryptionKeyWrapMetadata.Type, encryptionKeyWrapProvider.ProviderName)) { - throw new ArgumentException("The EncryptionKeyWrapMetadata Type value does not match with the ProviderName of EncryptionKeyStoreProvider configured on the Client. Please refer to https://aka.ms/CosmosClientEncryption for more details. "); + throw new ArgumentException("The EncryptionKeyWrapMetadata Type value does not match with the ProviderName of EncryptionKeyWrapProvider configured on the Client. Please refer to https://aka.ms/CosmosClientEncryption for more details. "); } ClientEncryptionKeyProperties clientEncryptionKeyProperties = await clientEncryptionKey.ReadAsync(cancellationToken: cancellationToken); @@ -162,14 +160,14 @@ public static async Task RewrapClientEncryptionKeyA KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate( clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Name, clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Value, - encryptionKeyStoreProvider); + encryptionKeyWrapProvider.EncryptionKeyStoreProviderImpl); byte[] unwrappedKey = keyEncryptionKey.DecryptEncryptionKey(clientEncryptionKeyProperties.WrappedDataEncryptionKey); keyEncryptionKey = KeyEncryptionKey.GetOrCreate( newEncryptionKeyWrapMetadata.Name, newEncryptionKeyWrapMetadata.Value, - encryptionKeyStoreProvider); + encryptionKeyWrapProvider.EncryptionKeyStoreProviderImpl); byte[] rewrappedKey = keyEncryptionKey.EncryptEncryptionKey(unwrappedKey); diff --git a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettingForProperty.cs b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettingForProperty.cs index 45b2a37c51..bf7103faa2 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettingForProperty.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettingForProperty.cs @@ -13,15 +13,13 @@ namespace Microsoft.Azure.Cosmos.Encryption internal sealed class EncryptionSettingForProperty { - private static readonly SemaphoreSlim EncryptionKeyCacheSemaphore = new SemaphoreSlim(1, 1); - private readonly string databaseRid; private readonly EncryptionContainer encryptionContainer; public EncryptionSettingForProperty( string clientEncryptionKeyId, - EncryptionType encryptionType, + Data.Encryption.Cryptography.EncryptionType encryptionType, EncryptionContainer encryptionContainer, string databaseRid) { @@ -33,7 +31,7 @@ public EncryptionSettingForProperty( public string ClientEncryptionKeyId { get; } - public EncryptionType EncryptionType { get; } + public Data.Encryption.Cryptography.EncryptionType EncryptionType { get; } public async Task BuildEncryptionAlgorithmForSettingAsync(CancellationToken cancellationToken) { @@ -52,7 +50,7 @@ public async Task BuildEncryptionAlgori // Here a request is sent out to unwrap using the Master Key configured via the Key Encryption Key. protectedDataEncryptionKey = await this.BuildProtectedDataEncryptionKeyAsync( clientEncryptionKeyProperties, - this.encryptionContainer.EncryptionCosmosClient.EncryptionKeyStoreProvider, + this.encryptionContainer.EncryptionCosmosClient.EncryptionKeyWrapProvider, this.ClientEncryptionKeyId, cancellationToken); } @@ -75,7 +73,7 @@ public async Task BuildEncryptionAlgori // try to build the ProtectedDataEncryptionKey. If it fails, try to force refresh the gateway cache and get the latest client encryption key. protectedDataEncryptionKey = await this.BuildProtectedDataEncryptionKeyAsync( clientEncryptionKeyProperties, - this.encryptionContainer.EncryptionCosmosClient.EncryptionKeyStoreProvider, + this.encryptionContainer.EncryptionCosmosClient.EncryptionKeyWrapProvider, this.ClientEncryptionKeyId, cancellationToken); } @@ -143,7 +141,7 @@ private async Task ForceRefreshGatewayCacheAndBuildP ProtectedDataEncryptionKey protectedDataEncryptionKey = await this.BuildProtectedDataEncryptionKeyAsync( clientEncryptionKeyProperties, - this.encryptionContainer.EncryptionCosmosClient.EncryptionKeyStoreProvider, + this.encryptionContainer.EncryptionCosmosClient.EncryptionKeyWrapProvider, this.ClientEncryptionKeyId, cancellationToken); @@ -152,18 +150,18 @@ private async Task ForceRefreshGatewayCacheAndBuildP private async Task BuildProtectedDataEncryptionKeyAsync( ClientEncryptionKeyProperties clientEncryptionKeyProperties, - EncryptionKeyStoreProvider encryptionKeyStoreProvider, + EncryptionKeyWrapProvider encryptionKeyWrapProvider, string keyId, CancellationToken cancellationToken) { - if (await EncryptionKeyCacheSemaphore.WaitAsync(-1, cancellationToken)) + if (await EncryptionCosmosClient.EncryptionKeyCacheSemaphore.WaitAsync(-1, cancellationToken)) { try { KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate( clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Name, clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Value, - encryptionKeyStoreProvider); + encryptionKeyWrapProvider.EncryptionKeyStoreProviderImpl); ProtectedDataEncryptionKey protectedDataEncryptionKey = ProtectedDataEncryptionKey.GetOrCreate( keyId, @@ -174,7 +172,7 @@ private async Task BuildProtectedDataEncryptionKeyAs } finally { - EncryptionKeyCacheSemaphore.Release(1); + EncryptionCosmosClient.EncryptionKeyCacheSemaphore.Release(1); } } diff --git a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettings.cs b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettings.cs index 5078fa3eb2..074d8a5c1f 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettings.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettings.cs @@ -49,12 +49,12 @@ public void SetRequestHeaders(RequestOptions requestOptions) }; } - private static EncryptionType GetEncryptionTypeForProperty(ClientEncryptionIncludedPath clientEncryptionIncludedPath) + private static Data.Encryption.Cryptography.EncryptionType GetEncryptionTypeForProperty(ClientEncryptionIncludedPath clientEncryptionIncludedPath) { return clientEncryptionIncludedPath.EncryptionType switch { - CosmosEncryptionType.Deterministic => EncryptionType.Deterministic, - CosmosEncryptionType.Randomized => EncryptionType.Randomized, + EncryptionType.Deterministic => Data.Encryption.Cryptography.EncryptionType.Deterministic, + EncryptionType.Randomized => Data.Encryption.Cryptography.EncryptionType.Randomized, _ => throw new ArgumentException($"Invalid encryption type {clientEncryptionIncludedPath.EncryptionType}. Please refer to https://aka.ms/CosmosClientEncryption for more details. "), }; } @@ -102,7 +102,7 @@ await encryptionContainer.EncryptionCosmosClient.GetClientEncryptionKeyPropertie // update the property level setting. foreach (ClientEncryptionIncludedPath propertyToEncrypt in clientEncryptionPolicy.IncludedPaths) { - EncryptionType encryptionType = GetEncryptionTypeForProperty(propertyToEncrypt); + Data.Encryption.Cryptography.EncryptionType encryptionType = GetEncryptionTypeForProperty(propertyToEncrypt); EncryptionSettingForProperty encryptionSettingsForProperty = new EncryptionSettingForProperty( propertyToEncrypt.ClientEncryptionKeyId, diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/AzureKeyVaultKeyWrapProvider.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/AzureKeyVaultKeyWrapProvider.cs new file mode 100644 index 0000000000..3945d380c1 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/AzureKeyVaultKeyWrapProvider.cs @@ -0,0 +1,77 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption +{ + using System; + using System.Threading.Tasks; + using global::Azure.Core; + using Microsoft.Data.Encryption.AzureKeyVaultProvider; + + /// + /// Implementation of key encryption key store provider that allows client applications to access data when a + /// key encryption key is stored in Microsoft Azure Key Vault. + /// + public sealed class AzureKeyVaultKeyWrapProvider : EncryptionKeyWrapProvider + { + private readonly AzureKeyVaultKeyStoreProvider azureKeyVaultKeyStoreProvider; + + /// + /// Initializes a new instance of the class. + /// Constructor that takes an implementation of Token Credential that is capable of providing an OAuth Token. + /// + /// returns token credentials. + public AzureKeyVaultKeyWrapProvider(TokenCredential tokenCredential) + { + // just rely on cache managed via EncryptionKeyWrapProvider. Setting DataEncryptionKeyCacheTimeToLive to zero results in not using azureKeyVaultKeyWrapProvider cache. + this.azureKeyVaultKeyStoreProvider = new AzureKeyVaultKeyStoreProvider(tokenCredential) + { + DataEncryptionKeyCacheTimeToLive = TimeSpan.Zero, + }; + } + + /// + /// Gets name of the Encryption Key Store Provider implementation. + /// + public override string ProviderName => this.azureKeyVaultKeyStoreProvider.ProviderName; + + /// + /// This function uses the asymmetric key specified by the key path + /// and decrypts an encrypted data dencryption key with RSA encryption algorithm. + /// . + /// Identifier of an asymmetric key in Azure Key Vault. + /// The key encryption algorithm. + /// The ciphertext key. + /// Plain text data encryption key. + public override Task UnwrapKeyAsync(string encryptionKeyId, string cosmosKeyEncryptionKeyAlgorithm, byte[] encryptedKey) + { + Data.Encryption.Cryptography.KeyEncryptionKeyAlgorithm keyEncryptionKeyAlgorithm = cosmosKeyEncryptionKeyAlgorithm switch + { + KeyEncryptionKeyAlgorithm.RsaOaep => Data.Encryption.Cryptography.KeyEncryptionKeyAlgorithm.RSA_OAEP, + _ => throw new NotSupportedException("The specified KeyEncryptionAlgorithm is not supported. Please refer to https://aka.ms/CosmosClientEncryption for more details. "), + }; + + return Task.FromResult(this.azureKeyVaultKeyStoreProvider.UnwrapKey(encryptionKeyId, keyEncryptionKeyAlgorithm, encryptedKey)); + } + + /// + /// This function uses the asymmetric key specified by the key path + /// and encrypts an unencrypted data encryption key with RSA encryption algorithm. + /// + /// Identifier of an asymmetric key in Azure Key Vault. + /// The key encryption algorithm. + /// The plaintext key. + /// Encrypted data encryption key. + public override Task WrapKeyAsync(string encryptionKeyId, string cosmosKeyEncryptionKeyAlgorithm, byte[] key) + { + Data.Encryption.Cryptography.KeyEncryptionKeyAlgorithm keyEncryptionKeyAlgorithm = cosmosKeyEncryptionKeyAlgorithm switch + { + KeyEncryptionKeyAlgorithm.RsaOaep => Data.Encryption.Cryptography.KeyEncryptionKeyAlgorithm.RSA_OAEP, + _ => throw new NotSupportedException("This specified KeyEncryptionAlgorithm is not supported. Please refer to https://aka.ms/CosmosClientEncryption for more details. "), + }; + + return Task.FromResult(this.azureKeyVaultKeyStoreProvider.WrapKey(encryptionKeyId, keyEncryptionKeyAlgorithm, key)); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/DataEncryptionKeyAlgorithm.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/DataEncryptionKeyAlgorithm.cs new file mode 100644 index 0000000000..e5216c1eb9 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/DataEncryptionKeyAlgorithm.cs @@ -0,0 +1,18 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption +{ + /// + /// Represents the encryption algorithms supported for data encryption. + /// + public static class DataEncryptionKeyAlgorithm + { + /// + /// Represents the authenticated encryption algorithm with associated data as described in + /// http://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05. + /// + public const string AeadAes256CbcHmacSha256 = "AEAD_AES_256_CBC_HMAC_SHA256"; + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionKeyStoreProviderImpl.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionKeyStoreProviderImpl.cs new file mode 100644 index 0000000000..45fc041933 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionKeyStoreProviderImpl.cs @@ -0,0 +1,74 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption +{ + using System; + using Microsoft.Data.Encryption.Cryptography; + + /// + /// The purpose/intention to introduce this class is to utilize the cache provide by the abstract class. This class basically + /// redirects all the corresponding calls to 's overridden methods and thus allowing us + /// to utilize the virtual method to access the cache. + /// + /// Note: Since and methods are not exposed, is not supported either. + /// + /// + /// The call hierarchy is as follows. Note, all core MDE API's used in internal cosmos encryption code are passed an EncryptionKeyStoreProviderImpl object. + /// ProtectedDataEncryptionKey -> KeyEncryptionKey(containing EncryptionKeyStoreProviderImpl object) -> EncryptionKeyStoreProviderImpl.WrapKey -> this.EncryptionKeyWrapProvider.WrapKeyAsync + /// ProtectedDataEncryptionKey -> KeyEncryptionKey(containing EncryptionKeyStoreProviderImpl object) -> EncryptionKeyStoreProviderImpl.UnWrapKey -> this.EncryptionKeyWrapProvider.UnwrapKeyAsync + /// + /// + internal class EncryptionKeyStoreProviderImpl : EncryptionKeyStoreProvider + { + private readonly EncryptionKeyWrapProvider encryptionKeyWrapProvider; + + public EncryptionKeyStoreProviderImpl(EncryptionKeyWrapProvider encryptionKeyWrapProvider) + { + this.encryptionKeyWrapProvider = encryptionKeyWrapProvider; + } + + public override string ProviderName => this.encryptionKeyWrapProvider.ProviderName; + + public override byte[] UnwrapKey(string encryptionKeyId, Data.Encryption.Cryptography.KeyEncryptionKeyAlgorithm algorithm, byte[] encryptedKey) + { + // since we do not expose GetOrCreateDataEncryptionKey we first look up the cache. + // Cache miss results in call to UnWrapCore which updates the cache after UnwrapKeyAsync is called. + return this.GetOrCreateDataEncryptionKey(encryptedKey.ToHexString(), UnWrapKeyCore); + + // delegate that is called by GetOrCreateDataEncryptionKey, which unwraps the key and updates the cache in case of cache miss. + byte[] UnWrapKeyCore() + { + return this.encryptionKeyWrapProvider.UnwrapKeyAsync(encryptionKeyId, algorithm.ToString(), encryptedKey) + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(); + } + } + + public override byte[] WrapKey(string encryptionKeyId, Data.Encryption.Cryptography.KeyEncryptionKeyAlgorithm algorithm, byte[] key) + { + return this.encryptionKeyWrapProvider.WrapKeyAsync(encryptionKeyId, algorithm.ToString(), key) + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(); + } + + /// + /// The public facing Cosmos Encryption library interface does not expose this method, hence not supported. + /// + public override byte[] Sign(string encryptionKeyId, bool allowEnclaveComputations) + { + throw new NotSupportedException("The Sign operation is not supported. "); + } + + /// + /// The public facing Cosmos Encryption library interface does not expose this method, hence not supported. + /// + public override bool Verify(string encryptionKeyId, bool allowEnclaveComputations, byte[] signature) + { + throw new NotSupportedException("The Verify operation is not supported. "); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionKeyWrapProvider.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionKeyWrapProvider.cs new file mode 100644 index 0000000000..408f060a7c --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionKeyWrapProvider.cs @@ -0,0 +1,92 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption +{ + using System; + using System.Threading.Tasks; + using Microsoft.Data.Encryption.Cryptography; + + /// + /// Base class for all key store providers. A custom provider must derive from this + /// class and override its member functions. + /// + public abstract class EncryptionKeyWrapProvider + { + internal EncryptionKeyWrapProvider() + { + this.EncryptionKeyStoreProviderImpl = new EncryptionKeyStoreProviderImpl(this); + } + + /// + /// Gets or sets the lifespan of the decrypted data encryption key in the cache. + /// Once the timespan has elapsed, the decrypted data encryption key is discarded + /// and must be revalidated. + /// + /// + /// Internally, there is a cache of key encryption keys (once they are unwrapped). + /// This is useful for rapidly decrypting multiple data values. The default value is 2 hours. + /// Setting the to zero disables caching. + /// + public TimeSpan? DataEncryptionKeyCacheTimeToLive + { + get => this.EncryptionKeyStoreProviderImpl.DataEncryptionKeyCacheTimeToLive; + set + { + this.EncryptionKeyStoreProviderImpl.DataEncryptionKeyCacheTimeToLive = value; + + // set the TTL for ProtectedDataEncryption, so that we have a uniform expiry of the KeyStoreProvider and ProtectedDataEncryption cache items. + if (this.EncryptionKeyStoreProviderImpl.DataEncryptionKeyCacheTimeToLive.HasValue) + { + if (EncryptionCosmosClient.EncryptionKeyCacheSemaphore.Wait(-1)) + { + try + { + // pick the min of the new value being set and ProtectedDataEncryptionKey's current TTL. Note ProtectedDataEncryptionKey TimeToLive is static + // and results in various instances to share this value. Hence we pick up whatever is the min value. If a TimeSpan.Zero is across any one instance + // it should be fine, since we look up the KeyStoreProvider cache. ProtectedDataEncryptionKey's own cache supersedes KeyStoreProvider cache, since it stores + // the RootKey which is derived from unwrapped key(Data Encryption Key). + // Note: DataEncryptionKeyCacheTimeToLive is nullable. When set to null this results in AbsoluteExpirationRelativeToNow to be set to null which caches forever. + // whatever is the current set value for ProtectedDataEncryptionKey TimeToLive(is not nullable) would be min if null value is passed. + if (TimeSpan.Compare(this.EncryptionKeyStoreProviderImpl.DataEncryptionKeyCacheTimeToLive.Value, ProtectedDataEncryptionKey.TimeToLive) < 0) + { + ProtectedDataEncryptionKey.TimeToLive = this.EncryptionKeyStoreProviderImpl.DataEncryptionKeyCacheTimeToLive.Value; + } + } + finally + { + EncryptionCosmosClient.EncryptionKeyCacheSemaphore.Release(1); + } + } + } + } + } + + /// + /// Gets the unique name that identifies a particular implementation of the abstract . + /// + public abstract string ProviderName { get; } + + internal EncryptionKeyStoreProviderImpl EncryptionKeyStoreProviderImpl { get; } + + /// + /// Unwraps the specified of a data encryption key. The encrypted value is expected to be encrypted using + /// the key encryption key with the specified and using the specified . + /// + /// The key Id tells the provider where to find the key. + /// The key encryption algorithm. + /// The ciphertext key. + /// The unwrapped data encryption key. + public abstract Task UnwrapKeyAsync(string encryptionKeyId, string keyEncryptionKeyAlgorithm, byte[] encryptedKey); + + /// + /// Wraps a data encryption key using the key encryption key with the specified and using the specified . + /// + /// The key Id tells the provider where to find the key. + /// The key encryption algorithm. + /// The plaintext key. + /// The wrapped data encryption key. + public abstract Task WrapKeyAsync(string encryptionKeyId, string keyEncryptionKeyAlgorithm, byte[] key); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/CosmosEncryptionType.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionType.cs similarity index 58% rename from Microsoft.Azure.Cosmos.Encryption/src/CosmosEncryptionType.cs rename to Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionType.cs index 111b54e3bf..f602f6e8b2 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/CosmosEncryptionType.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionType.cs @@ -5,9 +5,17 @@ namespace Microsoft.Azure.Cosmos.Encryption { /// - /// Algorithms for use with client-side encryption support in Azure Cosmos DB. + /// Represents the encryption algorithms supported for data encryption. /// - internal static class CosmosEncryptionType + /// + /// The type of data encryption. + /// + /// + /// The two encryption types are Deterministic and Randomized. + /// Deterministic encryption always generates the same encrypted value for any given plain text value. + /// Randomized encryption uses a method that encrypts data in a less predictable manner. Randomized encryption is more secure. + /// + public static class EncryptionType { /// /// Deterministic encryption always generates the same encrypted value for any given plain text value. diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/KeyEncryptionKeyAlgorithm.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/KeyEncryptionKeyAlgorithm.cs new file mode 100644 index 0000000000..255000884f --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/KeyEncryptionKeyAlgorithm.cs @@ -0,0 +1,17 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption +{ + /// + /// Represents the encryption algorithms supported for key encryption. + /// + public static class KeyEncryptionKeyAlgorithm + { + /// + /// RSA public key cryptography algorithm with Optimal Asymmetric Encryption Padding (OAEP) padding. + /// + public const string RsaOaep = "RSA_OAEP"; + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/Microsoft.Azure.Cosmos.Encryption.csproj b/Microsoft.Azure.Cosmos.Encryption/src/Microsoft.Azure.Cosmos.Encryption.csproj index 85bf7c8b48..b0a6d4465a 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/Microsoft.Azure.Cosmos.Encryption.csproj +++ b/Microsoft.Azure.Cosmos.Encryption/src/Microsoft.Azure.Cosmos.Encryption.csproj @@ -35,6 +35,7 @@ + diff --git a/Microsoft.Azure.Cosmos.Encryption/src/QueryDefinitionExtensions.cs b/Microsoft.Azure.Cosmos.Encryption/src/QueryDefinitionExtensions.cs index 808c8e8307..c9ee880032 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/QueryDefinitionExtensions.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/QueryDefinitionExtensions.cs @@ -88,7 +88,7 @@ public static async Task AddParameterAsync( return queryDefinitionwithEncryptedValues; } - if (settingsForProperty.EncryptionType == EncryptionType.Randomized) + if (settingsForProperty.EncryptionType == Data.Encryption.Cryptography.EncryptionType.Randomized) { throw new ArgumentException($"Unsupported argument with Path: {path} for query. For executing queries on encrypted path requires the use of deterministic encryption type. Please refer to https://aka.ms/CosmosClientEncryption for more details. "); } diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs b/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs index b44fe889e4..5eccc58c7a 100644 --- a/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs +++ b/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs @@ -10,13 +10,13 @@ namespace Microsoft.Azure.Cosmos.Encryption.EmulatorTests using System.IO; using System.Linq; using System.Net; + using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using global::Azure; using Microsoft.Azure.Cosmos; using Microsoft.Azure.Cosmos.Encryption; - using Microsoft.Data.Encryption.Cryptography; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -33,22 +33,22 @@ public class MdeEncryptionTests private static Database database; private static Container encryptionContainer; private static Container encryptionContainerForChangeFeed; - private static TestEncryptionKeyStoreProvider testEncryptionKeyStoreProvider; + private static TestEncryptionKeyWrapProvider testEncryptionKeyWrapProvider; [ClassInitialize] [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "The ClassInitialize method takes a single parameter of type TestContext.")] public static async Task ClassInitialize(TestContext context) { MdeEncryptionTests.client = TestCommon.CreateCosmosClient(); - testEncryptionKeyStoreProvider = new TestEncryptionKeyStoreProvider + testEncryptionKeyWrapProvider = new TestEncryptionKeyWrapProvider { DataEncryptionKeyCacheTimeToLive = null }; - metadata1 = new EncryptionKeyWrapMetadata(testEncryptionKeyStoreProvider.ProviderName, "key1", "tempmetadata1"); - metadata2 = new EncryptionKeyWrapMetadata(testEncryptionKeyStoreProvider.ProviderName, "key2", "tempmetadata2"); + metadata1 = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, "key1", "tempmetadata1"); + metadata2 = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, "key2", "tempmetadata2"); - MdeEncryptionTests.encryptionCosmosClient = MdeEncryptionTests.client.WithEncryption(testEncryptionKeyStoreProvider); + MdeEncryptionTests.encryptionCosmosClient = MdeEncryptionTests.client.WithEncryption(testEncryptionKeyWrapProvider); MdeEncryptionTests.database = await MdeEncryptionTests.encryptionCosmosClient.CreateDatabaseAsync(Guid.NewGuid().ToString()); await MdeEncryptionTests.CreateClientEncryptionKeyAsync( @@ -60,10 +60,10 @@ await MdeEncryptionTests.CreateClientEncryptionKeyAsync( metadata2); - EncryptionKeyWrapMetadata revokedKekmetadata = new EncryptionKeyWrapMetadata(testEncryptionKeyStoreProvider.ProviderName, "revokedKek", "revokedKek-metadata"); + EncryptionKeyWrapMetadata revokedKekmetadata = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, "revokedKek", "revokedKek-metadata"); await database.CreateClientEncryptionKeyAsync( "keywithRevokedKek", - DataEncryptionKeyAlgorithm.AEAD_AES_256_CBC_HMAC_SHA256, + DataEncryptionKeyAlgorithm.AeadAes256CbcHmacSha256, revokedKekmetadata); Collection paths = new Collection() @@ -183,7 +183,7 @@ private static async Task CreateClientEncryptionKey { ClientEncryptionKeyResponse clientEncrytionKeyResponse = await database.CreateClientEncryptionKeyAsync( cekId, - DataEncryptionKeyAlgorithm.AEAD_AES_256_CBC_HMAC_SHA256, + DataEncryptionKeyAlgorithm.AeadAes256CbcHmacSha256, encryptionKeyWrapMetadata); Assert.AreEqual(HttpStatusCode.Created, clientEncrytionKeyResponse.StatusCode); @@ -235,7 +235,7 @@ public async Task EncryptionBulkCrud() .WithBulkExecution(true) .Build()); - CosmosClient encryptionCosmosClientWithBulk = clientWithBulk.WithEncryption(new TestEncryptionKeyStoreProvider()); + CosmosClient encryptionCosmosClientWithBulk = clientWithBulk.WithEncryption(new TestEncryptionKeyWrapProvider()); Database databaseWithBulk = encryptionCosmosClientWithBulk.GetDatabase(MdeEncryptionTests.database.Id); Container encryptionContainerWithBulk = databaseWithBulk.GetContainer(MdeEncryptionTests.encryptionContainer.Id); @@ -257,18 +257,18 @@ public async Task EncryptionCreateClientEncryptionKey() { string cekId = "anotherCek"; - EncryptionKeyWrapMetadata metadata1 = new EncryptionKeyWrapMetadata(testEncryptionKeyStoreProvider.ProviderName, cekId, "testmetadata1"); + EncryptionKeyWrapMetadata metadata1 = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata1"); ClientEncryptionKeyProperties clientEncryptionKeyProperties = await MdeEncryptionTests.CreateClientEncryptionKeyAsync( cekId, metadata1); Assert.AreEqual( - new EncryptionKeyWrapMetadata(testEncryptionKeyStoreProvider.ProviderName, name: cekId, value: metadata1.Value), + new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, name: cekId, value: metadata1.Value), clientEncryptionKeyProperties.EncryptionKeyWrapMetadata); // creating another key with same id should fail - metadata1 = new EncryptionKeyWrapMetadata(testEncryptionKeyStoreProvider.ProviderName, cekId, "testmetadata2"); + metadata1 = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata2"); try { @@ -289,22 +289,22 @@ await MdeEncryptionTests.CreateClientEncryptionKeyAsync( public async Task EncryptionRewrapClientEncryptionKey() { string cekId = "rewrapkeytest"; - EncryptionKeyWrapMetadata metadata1 = new EncryptionKeyWrapMetadata(testEncryptionKeyStoreProvider.ProviderName, cekId, "testmetadata1"); + EncryptionKeyWrapMetadata metadata1 = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata1"); ClientEncryptionKeyProperties clientEncryptionKeyProperties = await MdeEncryptionTests.CreateClientEncryptionKeyAsync( cekId, metadata1); Assert.AreEqual( - new EncryptionKeyWrapMetadata(testEncryptionKeyStoreProvider.ProviderName, name: cekId, value: metadata1.Value), + new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, name: cekId, value: metadata1.Value), clientEncryptionKeyProperties.EncryptionKeyWrapMetadata); - EncryptionKeyWrapMetadata updatedMetaData = new EncryptionKeyWrapMetadata(testEncryptionKeyStoreProvider.ProviderName, cekId, metadata1 + "updatedmetadata"); + EncryptionKeyWrapMetadata updatedMetaData = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, metadata1 + "updatedmetadata"); clientEncryptionKeyProperties = await MdeEncryptionTests.RewarpClientEncryptionKeyAsync( cekId, updatedMetaData); Assert.AreEqual( - new EncryptionKeyWrapMetadata(testEncryptionKeyStoreProvider.ProviderName, name: cekId, value: updatedMetaData.Value), + new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, name: cekId, value: updatedMetaData.Value), clientEncryptionKeyProperties.EncryptionKeyWrapMetadata); } @@ -332,12 +332,12 @@ public async Task EncryptionCreateItemWithNullProperty() CosmosClient clientWithNoCaching = TestCommon.CreateCosmosClient(builder => builder .Build()); - TestEncryptionKeyStoreProvider testEncryptionKeyStoreProvider = new TestEncryptionKeyStoreProvider + TestEncryptionKeyWrapProvider testEncryptionKeyWrapProvider = new TestEncryptionKeyWrapProvider { DataEncryptionKeyCacheTimeToLive = TimeSpan.Zero }; - CosmosClient encryptionCosmosClient = clientWithNoCaching.WithEncryption(testEncryptionKeyStoreProvider); + CosmosClient encryptionCosmosClient = clientWithNoCaching.WithEncryption(testEncryptionKeyWrapProvider); Database database = encryptionCosmosClient.GetDatabase(MdeEncryptionTests.database.Id); Container encryptionContainer = database.GetContainer(MdeEncryptionTests.encryptionContainer.Id); @@ -395,7 +395,7 @@ await MdeEncryptionTests.ValidateQueryResultsAsync( expectedDoc: expectedDoc); // no access to key. - testEncryptionKeyStoreProvider.RevokeAccessSet = true; + testEncryptionKeyWrapProvider.RevokeAccessSet = true; testDoc = TestDoc.Create(); @@ -409,7 +409,7 @@ await MdeEncryptionTests.ValidateQueryResultsAsync( Assert.AreEqual(HttpStatusCode.Created, createResponse.StatusCode); VerifyExpectedDocResponse(testDoc, createResponse.Resource); - testEncryptionKeyStoreProvider.RevokeAccessSet = false; + testEncryptionKeyWrapProvider.RevokeAccessSet = false; } @@ -428,18 +428,18 @@ public async Task EncryptionResourceTokenAuthRestricted() restrictedUserPermission.Token); - CosmosClient encryptedclientForRestrictedUser = clientForRestrictedUser.WithEncryption(new TestEncryptionKeyStoreProvider()); + CosmosClient encryptedclientForRestrictedUser = clientForRestrictedUser.WithEncryption(new TestEncryptionKeyWrapProvider()); Database databaseForRestrictedUser = encryptedclientForRestrictedUser.GetDatabase(MdeEncryptionTests.database.Id); try { string cekId = "testingcekID"; - EncryptionKeyWrapMetadata metadata1 = new EncryptionKeyWrapMetadata(testEncryptionKeyStoreProvider.ProviderName, cekId, "testmetadata1"); + EncryptionKeyWrapMetadata metadata1 = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata1"); ClientEncryptionKeyResponse clientEncrytionKeyResponse = await databaseForRestrictedUser.CreateClientEncryptionKeyAsync( cekId, - DataEncryptionKeyAlgorithm.AEAD_AES_256_CBC_HMAC_SHA256, + DataEncryptionKeyAlgorithm.AeadAes256CbcHmacSha256, metadata1); Assert.Fail("CreateClientEncryptionKeyAsync should have failed due to restrictions"); } @@ -450,7 +450,7 @@ public async Task EncryptionResourceTokenAuthRestricted() try { string cekId = "testingcekID"; - EncryptionKeyWrapMetadata metadata1 = new EncryptionKeyWrapMetadata(testEncryptionKeyStoreProvider.ProviderName, cekId, "testmetadata1" + "updated"); + EncryptionKeyWrapMetadata metadata1 = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata1" + "updated"); ClientEncryptionKeyResponse clientEncrytionKeyResponse = await databaseForRestrictedUser.RewrapClientEncryptionKeyAsync( cekId, @@ -1001,7 +1001,7 @@ public async Task EncryptionValidatePolicyRefreshPostContainerDeleteWithBulk() .WithBulkExecution(false) .Build()); - CosmosClient otherEncryptionClient = otherClient.WithEncryption(new TestEncryptionKeyStoreProvider()); + CosmosClient otherEncryptionClient = otherClient.WithEncryption(new TestEncryptionKeyWrapProvider()); Database otherDatabase = otherEncryptionClient.GetDatabase(MdeEncryptionTests.database.Id); Container otherEncryptionContainer = otherDatabase.GetContainer(encryptionContainerToDelete.Id); @@ -1150,7 +1150,7 @@ public async Task EncryptionValidatePolicyRefreshPostContainerDeleteTransactionB CosmosClient otherClient = TestCommon.CreateCosmosClient(builder => builder .Build()); - CosmosClient otherEncryptionClient = otherClient.WithEncryption(new TestEncryptionKeyStoreProvider()); + CosmosClient otherEncryptionClient = otherClient.WithEncryption(new TestEncryptionKeyWrapProvider()); Database otherDatabase = otherEncryptionClient.GetDatabase(MdeEncryptionTests.database.Id); Container otherEncryptionContainer = otherDatabase.GetContainer(encryptionContainerToDelete.Id); @@ -1282,7 +1282,7 @@ public async Task EncryptionValidatePolicyRefreshPostContainerDeleteQuery() CosmosClient otherClient = TestCommon.CreateCosmosClient(builder => builder .Build()); - CosmosClient otherEncryptionClient = otherClient.WithEncryption(new TestEncryptionKeyStoreProvider()); + CosmosClient otherEncryptionClient = otherClient.WithEncryption(new TestEncryptionKeyWrapProvider()); Database otherDatabase = otherEncryptionClient.GetDatabase(MdeEncryptionTests.database.Id); Container otherEncryptionContainer = otherDatabase.GetContainer(encryptionContainerToDelete.Id); @@ -1397,7 +1397,7 @@ public async Task EncryptionValidatePolicyRefreshPostContainerDeletePatch() CosmosClient otherClient = TestCommon.CreateCosmosClient(builder => builder .Build()); - CosmosClient otherEncryptionClient = otherClient.WithEncryption(new TestEncryptionKeyStoreProvider()); + CosmosClient otherEncryptionClient = otherClient.WithEncryption(new TestEncryptionKeyWrapProvider()); Database otherDatabase = otherEncryptionClient.GetDatabase(MdeEncryptionTests.database.Id); Container otherEncryptionContainer = otherDatabase.GetContainer(encryptionContainerToDelete.Id); @@ -1544,18 +1544,18 @@ public async Task EncryptionValidatePolicyRefreshPostDatabaseDelete() CosmosClient mainClient = TestCommon.CreateCosmosClient(builder => builder .Build()); - TestEncryptionKeyStoreProvider testEncryptionKeyStoreProvider = new TestEncryptionKeyStoreProvider + TestEncryptionKeyWrapProvider testEncryptionKeyWrapProvider = new TestEncryptionKeyWrapProvider { DataEncryptionKeyCacheTimeToLive = TimeSpan.FromMinutes(30), }; - EncryptionKeyWrapMetadata keyWrapMetadata = new EncryptionKeyWrapMetadata(testEncryptionKeyStoreProvider.ProviderName, "myCek", "mymetadata1"); - CosmosClient encryptionCosmosClient = mainClient.WithEncryption(testEncryptionKeyStoreProvider); + EncryptionKeyWrapMetadata keyWrapMetadata = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, "myCek", "mymetadata1"); + CosmosClient encryptionCosmosClient = mainClient.WithEncryption(testEncryptionKeyWrapProvider); Database mainDatabase = await encryptionCosmosClient.CreateDatabaseAsync("databaseToBeDeleted"); ClientEncryptionKeyResponse clientEncrytionKeyResponse = await mainDatabase.CreateClientEncryptionKeyAsync( keyWrapMetadata.Name, - DataEncryptionKeyAlgorithm.AEAD_AES_256_CBC_HMAC_SHA256, + DataEncryptionKeyAlgorithm.AeadAes256CbcHmacSha256, keyWrapMetadata); Collection originalPaths = new Collection() @@ -1597,12 +1597,12 @@ public async Task EncryptionValidatePolicyRefreshPostDatabaseDelete() CosmosClient otherClient1 = TestCommon.CreateCosmosClient(builder => builder .Build()); - TestEncryptionKeyStoreProvider testEncryptionKeyStoreProvider2 = new TestEncryptionKeyStoreProvider + TestEncryptionKeyWrapProvider testEncryptionKeyWrapProvider2 = new TestEncryptionKeyWrapProvider { DataEncryptionKeyCacheTimeToLive = TimeSpan.Zero, }; - CosmosClient otherEncryptionClient = otherClient1.WithEncryption(testEncryptionKeyStoreProvider2); + CosmosClient otherEncryptionClient = otherClient1.WithEncryption(testEncryptionKeyWrapProvider2); Database otherDatabase = otherEncryptionClient.GetDatabase(mainDatabase.Id); Container otherEncryptionContainer = otherDatabase.GetContainer(encryptionContainerToDelete.Id); @@ -1615,10 +1615,10 @@ public async Task EncryptionValidatePolicyRefreshPostDatabaseDelete() mainDatabase = await encryptionCosmosClient.CreateDatabaseAsync("databaseToBeDeleted"); - keyWrapMetadata = new EncryptionKeyWrapMetadata(testEncryptionKeyStoreProvider2.ProviderName, "myCek", "mymetadata2"); + keyWrapMetadata = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider2.ProviderName, "myCek", "mymetadata2"); clientEncrytionKeyResponse = await mainDatabase.CreateClientEncryptionKeyAsync( keyWrapMetadata.Name, - DataEncryptionKeyAlgorithm.AEAD_AES_256_CBC_HMAC_SHA256, + DataEncryptionKeyAlgorithm.AeadAes256CbcHmacSha256, keyWrapMetadata); using (await mainDatabase.GetContainer(encryptionContainerToDelete.Id).DeleteContainerStreamAsync()) @@ -1723,12 +1723,12 @@ public async Task EncryptionValidatePolicyRefreshPostDatabaseDelete() .WithBulkExecution(true) .Build()); - TestEncryptionKeyStoreProvider testEncryptionKeyStoreProvider3 = new TestEncryptionKeyStoreProvider + TestEncryptionKeyWrapProvider testEncryptionKeyWrapProvider3 = new TestEncryptionKeyWrapProvider { DataEncryptionKeyCacheTimeToLive = TimeSpan.FromMinutes(30), }; - CosmosClient otherEncryptionClient2 = otherClient2.WithEncryption(testEncryptionKeyStoreProvider3); + CosmosClient otherEncryptionClient2 = otherClient2.WithEncryption(testEncryptionKeyWrapProvider3); Database otherDatabase2 = otherEncryptionClient2.GetDatabase(mainDatabase.Id); Container otherEncryptionContainer3 = otherDatabase2.GetContainer(otherEncryptionContainer2.Id); @@ -1774,18 +1774,102 @@ public async Task EncryptionValidatePolicyRefreshPostDatabaseDelete() await mainClient.GetDatabase("databaseToBeDeleted").DeleteStreamAsync(); } + [TestMethod] + public void MdeEncryptionTypesContractTest() + { + string[] cosmosSupportedEncryptionTypes = typeof(EncryptionType) + .GetMembers(BindingFlags.Static | BindingFlags.Public) + .Select(e => ((FieldInfo)e).GetValue(e).ToString()) + .ToArray(); + + string[] mdeSupportedEncryptionTypes = typeof(Data.Encryption.Cryptography.EncryptionType) + .GetMembers(BindingFlags.Static | BindingFlags.Public) + .Select(e => e.Name) + .ToArray(); + + if (mdeSupportedEncryptionTypes.Length > cosmosSupportedEncryptionTypes.Length) + { + HashSet missingEncryptionTypes = new HashSet(mdeSupportedEncryptionTypes); + foreach (string encryptionTypes in cosmosSupportedEncryptionTypes) + { + missingEncryptionTypes.Remove(encryptionTypes); + } + + // no Plaintext support. + missingEncryptionTypes.Remove("Plaintext"); + mdeSupportedEncryptionTypes = mdeSupportedEncryptionTypes.Where(value => value != "Plaintext").ToArray(); + + if (missingEncryptionTypes.Count != 0) + { + Assert.Fail($"Missing EncryptionType support from CosmosEncryptionType: {string.Join(";", missingEncryptionTypes)}"); + } + } + + CollectionAssert.AreEquivalent(mdeSupportedEncryptionTypes, cosmosSupportedEncryptionTypes); + } + + [TestMethod] + public void MdeEncryptionAlgorithmsTypesContractTest() + { + string[] cosmosKeyEncryptionAlgorithms = typeof(KeyEncryptionKeyAlgorithm) + .GetMembers(BindingFlags.Static | BindingFlags.Public) + .Select(e => ((FieldInfo)e).GetValue(e).ToString()) + .ToArray(); + + string[] mdeKeyEncryptionAlgorithms = typeof(Data.Encryption.Cryptography.KeyEncryptionKeyAlgorithm) + .GetMembers(BindingFlags.Static | BindingFlags.Public) + .Select(e => e.Name) + .ToArray(); + + if (mdeKeyEncryptionAlgorithms.Length > cosmosKeyEncryptionAlgorithms.Length) + { + HashSet missingKeyEncryptionAlgorithms = new HashSet(mdeKeyEncryptionAlgorithms); + foreach (string algorithm in cosmosKeyEncryptionAlgorithms) + { + missingKeyEncryptionAlgorithms.Remove(algorithm); + } + + Assert.Fail($"Missing key encryption algorithm support from CosmosKeyEncryptionKeyAlgorithm: {string.Join(";", missingKeyEncryptionAlgorithms)}"); + } + + CollectionAssert.AreEquivalent(mdeKeyEncryptionAlgorithms, cosmosKeyEncryptionAlgorithms); + + string[] cosmosDataEncryptionAlgorithms = typeof(DataEncryptionKeyAlgorithm) + .GetMembers(BindingFlags.Static | BindingFlags.Public) + .Select(e => ((FieldInfo)e).GetValue(e).ToString()) + .ToArray(); + + string[] mdeDataEncryptionAlgorithms = typeof(Data.Encryption.Cryptography.DataEncryptionKeyAlgorithm) + .GetMembers(BindingFlags.Static | BindingFlags.Public) + .Select(e => e.Name) + .ToArray(); + + if (mdeDataEncryptionAlgorithms.Length > cosmosDataEncryptionAlgorithms.Length) + { + HashSet missingDataEncryptionAlgorithms = new HashSet(mdeDataEncryptionAlgorithms); + foreach (string algorithm in cosmosDataEncryptionAlgorithms) + { + missingDataEncryptionAlgorithms.Remove(algorithm); + } + + Assert.Fail($"Missing data encryption algorithm support from CosmosDataEncryptionKeyAlgorithm: {string.Join(";", missingDataEncryptionAlgorithms)}"); + } + + CollectionAssert.AreEquivalent(mdeDataEncryptionAlgorithms, cosmosDataEncryptionAlgorithms); + } + [TestMethod] public async Task VerifyKekRevokeHandling() { CosmosClient clientWithNoCaching = TestCommon.CreateCosmosClient(builder => builder .Build()); - TestEncryptionKeyStoreProvider testEncryptionKeyStoreProvider = new TestEncryptionKeyStoreProvider + TestEncryptionKeyWrapProvider testEncryptionKeyWrapProvider = new TestEncryptionKeyWrapProvider { DataEncryptionKeyCacheTimeToLive = TimeSpan.Zero }; - CosmosClient encryptionCosmosClient = clientWithNoCaching.WithEncryption(testEncryptionKeyStoreProvider); + CosmosClient encryptionCosmosClient = clientWithNoCaching.WithEncryption(testEncryptionKeyWrapProvider); Database database = encryptionCosmosClient.GetDatabase(MdeEncryptionTests.database.Id); // Once a Dek gets cached and the Kek is revoked, calls to unwrap/wrap keys would fail since KEK is revoked. @@ -1809,7 +1893,7 @@ public async Task VerifyKekRevokeHandling() TestDoc testDoc1 = await MdeEncryptionTests.MdeCreateItemAsync(encryptionContainer); - testEncryptionKeyStoreProvider.RevokeAccessSet = true; + testEncryptionKeyWrapProvider.RevokeAccessSet = true; // try creating it and it should fail as it has been revoked. try @@ -1837,16 +1921,16 @@ await MdeEncryptionTests.ValidateQueryResultsAsync( } // for unwrap to succeed - testEncryptionKeyStoreProvider.RevokeAccessSet = false; + testEncryptionKeyWrapProvider.RevokeAccessSet = false; // lets rewrap it. await database.RewrapClientEncryptionKeyAsync("keywithRevokedKek", MdeEncryptionTests.metadata2); - testEncryptionKeyStoreProvider.RevokeAccessSet = true; + testEncryptionKeyWrapProvider.RevokeAccessSet = true; // Should fail but will try to fetch the lastest from the Backend and updates the cache. await MdeEncryptionTests.MdeCreateItemAsync(encryptionContainer); - testEncryptionKeyStoreProvider.RevokeAccessSet = false; - testEncryptionKeyStoreProvider.DataEncryptionKeyCacheTimeToLive = TimeSpan.FromMinutes(120); + testEncryptionKeyWrapProvider.RevokeAccessSet = false; + testEncryptionKeyWrapProvider.DataEncryptionKeyCacheTimeToLive = TimeSpan.FromMinutes(120); } [TestMethod] @@ -2126,7 +2210,7 @@ public async Task EncryptionTransactionalBatchWithCustomSerializer() .WithCustomSerializer(customSerializer) .Build()); - CosmosClient encryptionCosmosClientWithCustomSerializer = clientWithCustomSerializer.WithEncryption(new TestEncryptionKeyStoreProvider()); + CosmosClient encryptionCosmosClientWithCustomSerializer = clientWithCustomSerializer.WithEncryption(new TestEncryptionKeyWrapProvider()); Database databaseWithCustomSerializer = encryptionCosmosClientWithCustomSerializer.GetDatabase(MdeEncryptionTests.database.Id); Container encryptionContainerWithCustomSerializer = databaseWithCustomSerializer.GetContainer(MdeEncryptionTests.encryptionContainer.Id); @@ -2184,8 +2268,8 @@ await MdeEncryptionTests.ValidateQueryResultsAsync( public async Task ValidateCachingofProtectedDataEncryptionKey() { // Default cache TTL 2 hours. - TestEncryptionKeyStoreProvider newtestEncryptionKeyStoreProvider = new TestEncryptionKeyStoreProvider(); - CosmosClient newEncryptionClient = MdeEncryptionTests.client.WithEncryption(newtestEncryptionKeyStoreProvider); + TestEncryptionKeyWrapProvider newtestEncryptionKeyWrapProvider = new TestEncryptionKeyWrapProvider(); + CosmosClient newEncryptionClient = MdeEncryptionTests.client.WithEncryption(newtestEncryptionKeyWrapProvider); Database database = newEncryptionClient.GetDatabase(MdeEncryptionTests.database.Id); Container encryptionContainer = database.GetContainer(MdeEncryptionTests.encryptionContainer.Id); @@ -2195,17 +2279,13 @@ public async Task ValidateCachingofProtectedDataEncryptionKey() await MdeEncryptionTests.MdeCreateItemAsync(encryptionContainer); } - newtestEncryptionKeyStoreProvider.UnWrapKeyCallsCount.TryGetValue(metadata1.Value, out int unwrapcount); + newtestEncryptionKeyWrapProvider.UnWrapKeyCallsCount.TryGetValue(metadata1.Value, out int unwrapcount); // expecting just one unwrap. Assert.AreEqual(1, unwrapcount); // no caching. - newtestEncryptionKeyStoreProvider = new TestEncryptionKeyStoreProvider() - { - DataEncryptionKeyCacheTimeToLive = TimeSpan.Zero, - }; + newtestEncryptionKeyWrapProvider.DataEncryptionKeyCacheTimeToLive = TimeSpan.Zero; - newEncryptionClient = MdeEncryptionTests.client.WithEncryption(newtestEncryptionKeyStoreProvider); database = newEncryptionClient.GetDatabase(MdeEncryptionTests.database.Id); encryptionContainer = database.GetContainer(MdeEncryptionTests.encryptionContainer.Id); @@ -2215,7 +2295,7 @@ public async Task ValidateCachingofProtectedDataEncryptionKey() await MdeEncryptionTests.MdeCreateItemAsync(encryptionContainer); } - newtestEncryptionKeyStoreProvider.UnWrapKeyCallsCount.TryGetValue(metadata1.Value, out unwrapcount); + newtestEncryptionKeyWrapProvider.UnWrapKeyCallsCount.TryGetValue(metadata1.Value, out unwrapcount); Assert.IsTrue(unwrapcount > 1, "The actual unwrap count was not greater than 1"); } @@ -3214,7 +3294,7 @@ public Stream ToStream() } } - internal class TestEncryptionKeyStoreProvider : EncryptionKeyStoreProvider + internal class TestEncryptionKeyWrapProvider : EncryptionKeyWrapProvider { readonly Dictionary keyinfo = new Dictionary { @@ -3223,9 +3303,12 @@ internal class TestEncryptionKeyStoreProvider : EncryptionKeyStoreProvider }; public bool RevokeAccessSet { get; set; } + public Dictionary WrapKeyCallsCount { get; set; } + public Dictionary UnWrapKeyCallsCount { get; set; } - public TestEncryptionKeyStoreProvider() + + public TestEncryptionKeyWrapProvider() { this.WrapKeyCallsCount = new Dictionary(); this.UnWrapKeyCallsCount = new Dictionary(); @@ -3234,33 +3317,28 @@ public TestEncryptionKeyStoreProvider() public override string ProviderName => "TESTKEYSTORE_VAULT"; - public override byte[] UnwrapKey(string masterKeyPath, KeyEncryptionKeyAlgorithm encryptionAlgorithm, byte[] encryptedKey) + public override Task UnwrapKeyAsync(string masterKeyPath, string keyEncryptionKeyAlgorithm, byte[] encryptedKey) { if (masterKeyPath.Equals("revokedKek-metadata") && this.RevokeAccessSet) { throw new RequestFailedException((int)HttpStatusCode.Forbidden, "Forbidden"); } - return this.GetOrCreateDataEncryptionKey(encryptedKey.ToHexString(), DecryptEncryptionKey); - - byte[] DecryptEncryptionKey() + if (!this.UnWrapKeyCallsCount.ContainsKey(masterKeyPath)) { - if (!this.UnWrapKeyCallsCount.ContainsKey(masterKeyPath)) - { - this.UnWrapKeyCallsCount[masterKeyPath] = 1; - } - else - { - this.UnWrapKeyCallsCount[masterKeyPath]++; - } - - this.keyinfo.TryGetValue(masterKeyPath, out int moveBy); - byte[] plainkey = encryptedKey.Select(b => (byte)(b - moveBy)).ToArray(); - return plainkey; + this.UnWrapKeyCallsCount[masterKeyPath] = 1; } + else + { + this.UnWrapKeyCallsCount[masterKeyPath]++; + } + + this.keyinfo.TryGetValue(masterKeyPath, out int moveBy); + byte[] plainkey = encryptedKey.Select(b => (byte)(b - moveBy)).ToArray(); + return Task.FromResult(plainkey); } - public override byte[] WrapKey(string masterKeyPath, KeyEncryptionKeyAlgorithm encryptionAlgorithm, byte[] key) + public override Task WrapKeyAsync(string masterKeyPath, string keyEncryptionKeyAlgorithm, byte[] key) { if (!this.WrapKeyCallsCount.ContainsKey(masterKeyPath)) { @@ -3273,17 +3351,7 @@ public override byte[] WrapKey(string masterKeyPath, KeyEncryptionKeyAlgorithm e this.keyinfo.TryGetValue(masterKeyPath, out int moveBy); byte[] encryptedkey = key.Select(b => (byte)(b + moveBy)).ToArray(); - return encryptedkey; - } - - public override byte[] Sign(string masterKeyPath, bool allowEnclaveComputations) - { - return null; - } - - public override bool Verify(string masterKeyPath, bool allowEnclaveComputations, byte[] signature) - { - return true; + return Task.FromResult(encryptedkey); } } diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Contracts/DotNetSDKEncryptionAPI.json b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Contracts/DotNetSDKEncryptionAPI.json index 14887781c5..1f53d62344 100644 --- a/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Contracts/DotNetSDKEncryptionAPI.json +++ b/Microsoft.Azure.Cosmos.Encryption/tests/Microsoft.Azure.Cosmos.Encryption.Tests/Contracts/DotNetSDKEncryptionAPI.json @@ -1,5 +1,47 @@ { "Subclasses": { + "Microsoft.Azure.Cosmos.Encryption.AzureKeyVaultKeyWrapProvider;Microsoft.Azure.Cosmos.Encryption.EncryptionKeyWrapProvider;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String get_ProviderName()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.String get_ProviderName();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String ProviderName": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.String ProviderName;CanRead:True;CanWrite:False;System.String get_ProviderName();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[System.Byte[]] UnwrapKeyAsync(System.String, System.String, Byte[])": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[System.Byte[]] UnwrapKeyAsync(System.String, System.String, Byte[]);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[System.Byte[]] WrapKeyAsync(System.String, System.String, Byte[])": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[System.Byte[]] WrapKeyAsync(System.String, System.String, Byte[]);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(Azure.Core.TokenCredential)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "[Void .ctor(Azure.Core.TokenCredential), Void .ctor(Azure.Core.TokenCredential)]" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.DataEncryptionKeyAlgorithm;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String AeadAes256CbcHmacSha256": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String AeadAes256CbcHmacSha256;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, "Microsoft.Azure.Cosmos.Encryption.EncryptionContainerExtensions;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { "Subclasses": {}, "Members": { @@ -38,12 +80,12 @@ "Microsoft.Azure.Cosmos.Encryption.EncryptionCosmosClientExtensions;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { "Subclasses": {}, "Members": { - "Microsoft.Azure.Cosmos.CosmosClient WithEncryption(Microsoft.Azure.Cosmos.CosmosClient, Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Microsoft.Azure.Cosmos.CosmosClient WithEncryption(Microsoft.Azure.Cosmos.CosmosClient, Microsoft.Azure.Cosmos.Encryption.EncryptionKeyWrapProvider)[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "MethodInfo": "Microsoft.Azure.Cosmos.CosmosClient WithEncryption(Microsoft.Azure.Cosmos.CosmosClient, Microsoft.Data.Encryption.Cryptography.EncryptionKeyStoreProvider);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "Microsoft.Azure.Cosmos.CosmosClient WithEncryption(Microsoft.Azure.Cosmos.CosmosClient, Microsoft.Azure.Cosmos.Encryption.EncryptionKeyWrapProvider);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" } }, "NestedTypes": {} @@ -51,13 +93,13 @@ "Microsoft.Azure.Cosmos.Encryption.EncryptionDatabaseExtensions;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { "Subclasses": {}, "Members": { - "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse] CreateClientEncryptionKeyAsync(Microsoft.Azure.Cosmos.Database, System.String, Microsoft.Data.Encryption.Cryptography.DataEncryptionKeyAlgorithm, Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.EncryptionDatabaseExtensions+))]-[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse] CreateClientEncryptionKeyAsync(Microsoft.Azure.Cosmos.Database, System.String, System.String, Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.EncryptionDatabaseExtensions+))]-[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "AsyncStateMachineAttribute", "ExtensionAttribute" ], - "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse] CreateClientEncryptionKeyAsync(Microsoft.Azure.Cosmos.Database, System.String, Microsoft.Data.Encryption.Cryptography.DataEncryptionKeyAlgorithm, Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata, System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse] CreateClientEncryptionKeyAsync(Microsoft.Azure.Cosmos.Database, System.String, System.String, Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata, System.Threading.CancellationToken);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.ClientEncryptionKeyResponse] RewrapClientEncryptionKeyAsync(Microsoft.Azure.Cosmos.Database, System.String, Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata, System.Threading.CancellationToken)[System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Microsoft.Azure.Cosmos.Encryption.EncryptionDatabaseExtensions+))]-[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", @@ -70,6 +112,106 @@ }, "NestedTypes": {} }, + "Microsoft.Azure.Cosmos.Encryption.EncryptionKeyWrapProvider;System.Object;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": { + "Microsoft.Azure.Cosmos.Encryption.AzureKeyVaultKeyWrapProvider;Microsoft.Azure.Cosmos.Encryption.EncryptionKeyWrapProvider;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String get_ProviderName()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.String get_ProviderName();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String ProviderName": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.String ProviderName;CanRead:True;CanWrite:False;System.String get_ProviderName();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[System.Byte[]] UnwrapKeyAsync(System.String, System.String, Byte[])": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[System.Byte[]] UnwrapKeyAsync(System.String, System.String, Byte[]);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[System.Byte[]] WrapKeyAsync(System.String, System.String, Byte[])": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[System.Byte[]] WrapKeyAsync(System.String, System.String, Byte[]);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void .ctor(Azure.Core.TokenCredential)": { + "Type": "Constructor", + "Attributes": [], + "MethodInfo": "[Void .ctor(Azure.Core.TokenCredential), Void .ctor(Azure.Core.TokenCredential)]" + } + }, + "NestedTypes": {} + } + }, + "Members": { + "System.Nullable`1[System.TimeSpan] DataEncryptionKeyCacheTimeToLive": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.Nullable`1[System.TimeSpan] DataEncryptionKeyCacheTimeToLive;CanRead:True;CanWrite:True;System.Nullable`1[System.TimeSpan] get_DataEncryptionKeyCacheTimeToLive();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_DataEncryptionKeyCacheTimeToLive(System.Nullable`1[System.TimeSpan]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[System.TimeSpan] get_DataEncryptionKeyCacheTimeToLive()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Nullable`1[System.TimeSpan] get_DataEncryptionKeyCacheTimeToLive();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_ProviderName()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.String get_ProviderName();IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String ProviderName": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.String ProviderName;CanRead:True;CanWrite:False;System.String get_ProviderName();IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[System.Byte[]] UnwrapKeyAsync(System.String, System.String, Byte[])": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[System.Byte[]] UnwrapKeyAsync(System.String, System.String, Byte[]);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Threading.Tasks.Task`1[System.Byte[]] WrapKeyAsync(System.String, System.String, Byte[])": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "System.Threading.Tasks.Task`1[System.Byte[]] WrapKeyAsync(System.String, System.String, Byte[]);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_DataEncryptionKeyCacheTimeToLive(System.Nullable`1[System.TimeSpan])": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Void set_DataEncryptionKeyCacheTimeToLive(System.Nullable`1[System.TimeSpan]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.EncryptionType;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String Deterministic": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Deterministic;IsInitOnly:False;IsStatic:True;" + }, + "System.String Randomized": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String Randomized;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, + "Microsoft.Azure.Cosmos.Encryption.KeyEncryptionKeyAlgorithm;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { + "Subclasses": {}, + "Members": { + "System.String RsaOaep": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "System.String RsaOaep;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, "Microsoft.Azure.Cosmos.Encryption.QueryDefinitionExtensions;System.Object;IsAbstract:True;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { "Subclasses": {}, "Members": {