From 6156c4c93741a581355d557a7661ee382935e0fd Mon Sep 17 00:00:00 2001 From: abhijitpai Date: Thu, 3 Mar 2022 19:06:49 +0530 Subject: [PATCH] ClientEncryption: Adds the use of Azure.Core interfaces in public surface (#3050) Use interfaces from Azure.Core.Cryptography for customers to provide the implementation used to wrap/unwrap the data encryption key instead of the existing custom wrapper interfaces created over Microsoft.Data.Encryption.Cryptography. This allows using the Azure.Security.KeyVault.Keys implementation for CMKs in Azure Key Vault and we no longer need the MDE specific Microsoft.Data.Encryption.AzureKeyVaultProvider. --- ...KeyAlgorithm.cs => EncryptionAlgorithm.cs} | 2 +- .../src/EncryptionCosmosClient.cs | 37 +- .../src/EncryptionCosmosClientExtensions.cs | 20 +- .../src/EncryptionDatabaseExtensions.cs | 16 +- .../EncryptionKeyStoreProviderImpl.cs | 49 ++- .../src/EncryptionSettingForProperty.cs | 6 +- .../src/{MdeSupport => }/EncryptionType.cs | 0 .../src/KeyEncryptionKeyResolverName.cs | 19 + .../AzureKeyVaultKeyStoreProvider.cs | 352 ------------------ .../MdeSrc/AzureKeyVaultProvider/Constants.cs | 26 -- .../AzureKeyVaultProvider/KeyCryptographer.cs | 226 ----------- .../AzureKeyVaultKeyWrapProvider.cs | 77 ---- .../MdeSupport/EncryptionKeyWrapProvider.cs | 92 ----- .../MdeSupport/KeyEncryptionKeyAlgorithm.cs | 17 - .../Microsoft.Azure.Cosmos.Encryption.csproj | 1 - .../tests/EmulatorTests/MdeEncryptionTests.cs | 285 +++++++------- .../Contracts/DotNetSDKEncryptionAPI.json | 116 +----- 17 files changed, 243 insertions(+), 1098 deletions(-) rename Microsoft.Azure.Cosmos.Encryption/src/{MdeSupport/DataEncryptionKeyAlgorithm.cs => EncryptionAlgorithm.cs} (92%) rename Microsoft.Azure.Cosmos.Encryption/src/{MdeSupport => }/EncryptionKeyStoreProviderImpl.cs (61%) rename Microsoft.Azure.Cosmos.Encryption/src/{MdeSupport => }/EncryptionType.cs (100%) create mode 100644 Microsoft.Azure.Cosmos.Encryption/src/KeyEncryptionKeyResolverName.cs delete mode 100644 Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/AzureKeyVaultKeyStoreProvider.cs delete mode 100644 Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/Constants.cs delete mode 100644 Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/KeyCryptographer.cs delete mode 100644 Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/AzureKeyVaultKeyWrapProvider.cs delete mode 100644 Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionKeyWrapProvider.cs delete mode 100644 Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/KeyEncryptionKeyAlgorithm.cs diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/DataEncryptionKeyAlgorithm.cs b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionAlgorithm.cs similarity index 92% rename from Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/DataEncryptionKeyAlgorithm.cs rename to Microsoft.Azure.Cosmos.Encryption/src/EncryptionAlgorithm.cs index e5216c1eb9..67b91829dd 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/DataEncryptionKeyAlgorithm.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionAlgorithm.cs @@ -7,7 +7,7 @@ namespace Microsoft.Azure.Cosmos.Encryption /// /// Represents the encryption algorithms supported for data encryption. /// - public static class DataEncryptionKeyAlgorithm + public static class EncryptionAlgorithm { /// /// Represents the authenticated encryption algorithm with associated data as described in diff --git a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionCosmosClient.cs b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionCosmosClient.cs index 89dfc4b7bd..9864fbd65f 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionCosmosClient.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionCosmosClient.cs @@ -8,6 +8,8 @@ namespace Microsoft.Azure.Cosmos.Encryption using System.Net; using System.Threading; using System.Threading.Tasks; + using global::Azure.Core.Cryptography; + using Microsoft.Data.Encryption.Cryptography; /// /// CosmosClient with Encryption support. @@ -20,14 +22,43 @@ internal sealed class EncryptionCosmosClient : CosmosClient private readonly AsyncCache clientEncryptionKeyPropertiesCacheByKeyId; - public EncryptionCosmosClient(CosmosClient cosmosClient, EncryptionKeyWrapProvider encryptionKeyWrapProvider) + public EncryptionCosmosClient( + CosmosClient cosmosClient, + IKeyEncryptionKeyResolver keyEncryptionKeyResolver, + string keyEncryptionKeyResolverName, + TimeSpan? keyCacheTimeToLive) { this.cosmosClient = cosmosClient ?? throw new ArgumentNullException(nameof(cosmosClient)); - this.EncryptionKeyWrapProvider = encryptionKeyWrapProvider ?? throw new ArgumentNullException(nameof(encryptionKeyWrapProvider)); + this.KeyEncryptionKeyResolver = keyEncryptionKeyResolver ?? throw new ArgumentNullException(nameof(keyEncryptionKeyResolver)); + this.KeyEncryptionKeyResolverName = keyEncryptionKeyResolverName ?? throw new ArgumentNullException(nameof(keyEncryptionKeyResolverName)); this.clientEncryptionKeyPropertiesCacheByKeyId = new AsyncCache(); + this.EncryptionKeyStoreProviderImpl = new EncryptionKeyStoreProviderImpl(keyEncryptionKeyResolver, keyEncryptionKeyResolverName); + + keyCacheTimeToLive ??= TimeSpan.FromHours(1); + + if (EncryptionCosmosClient.EncryptionKeyCacheSemaphore.Wait(-1)) + { + try + { + // We pick the minimum between the existing and passed in value given this is a static cache. + // This also means that the maximum cache duration is the originally initialized value for ProtectedDataEncryptionKey.TimeToLive which is 2 hours. + if (keyCacheTimeToLive < ProtectedDataEncryptionKey.TimeToLive) + { + ProtectedDataEncryptionKey.TimeToLive = keyCacheTimeToLive.Value; + } + } + finally + { + EncryptionCosmosClient.EncryptionKeyCacheSemaphore.Release(1); + } + } } - public EncryptionKeyWrapProvider EncryptionKeyWrapProvider { get; } + public EncryptionKeyStoreProviderImpl EncryptionKeyStoreProviderImpl { get; } + + public IKeyEncryptionKeyResolver KeyEncryptionKeyResolver { get; } + + public string KeyEncryptionKeyResolverName { 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 ad2077f3dc..164c796d8b 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionCosmosClientExtensions.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionCosmosClientExtensions.cs @@ -5,6 +5,7 @@ namespace Microsoft.Azure.Cosmos.Encryption { using System; + using global::Azure.Core.Cryptography; using Microsoft.Data.Encryption.Cryptography; /// @@ -16,15 +17,24 @@ public static class EncryptionCosmosClientExtensions /// Get Cosmos Client with Encryption support for performing operations using client-side encryption. /// /// Regular Cosmos Client. - /// EncryptionKeyWrapProvider, provider that allows interaction with the master keys. + /// IKeyEncryptionKeyResolver that allows interaction with the key encryption keys. + /// Identifier of the resolver, eg. KeyEncryptionKeyResolverId.AzureKeyVault. + /// Time for which raw keys are cached in-memory. Defaults to 1 hour. /// CosmosClient to perform operations supporting client-side encryption / decryption. public static CosmosClient WithEncryption( this CosmosClient cosmosClient, - EncryptionKeyWrapProvider encryptionKeyWrapProvider) + IKeyEncryptionKeyResolver keyEncryptionKeyResolver, + string keyEncryptionKeyResolverId, + TimeSpan? keyCacheTimeToLive = null) { - if (encryptionKeyWrapProvider == null) + if (keyEncryptionKeyResolver == null) { - throw new ArgumentNullException(nameof(encryptionKeyWrapProvider)); + throw new ArgumentNullException(nameof(keyEncryptionKeyResolver)); + } + + if (keyEncryptionKeyResolverId == null) + { + throw new ArgumentNullException(nameof(keyEncryptionKeyResolverId)); } if (cosmosClient == null) @@ -32,7 +42,7 @@ public static CosmosClient WithEncryption( throw new ArgumentNullException(nameof(cosmosClient)); } - return new EncryptionCosmosClient(cosmosClient, encryptionKeyWrapProvider); + return new EncryptionCosmosClient(cosmosClient, keyEncryptionKeyResolver, keyEncryptionKeyResolverId, keyCacheTimeToLive); } } } diff --git a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionDatabaseExtensions.cs b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionDatabaseExtensions.cs index 417c9f3856..f81486b784 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionDatabaseExtensions.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionDatabaseExtensions.cs @@ -49,7 +49,7 @@ public static async Task CreateClientEncryptionKeyA throw new ArgumentNullException(nameof(clientEncryptionKeyId)); } - if (!string.Equals(dataEncryptionKeyAlgorithm, DataEncryptionKeyAlgorithm.AeadAes256CbcHmacSha256)) + if (!string.Equals(dataEncryptionKeyAlgorithm, EncryptionAlgorithm.AeadAes256CbcHmacSha256)) { throw new ArgumentException($"Invalid Encryption Algorithm '{dataEncryptionKeyAlgorithm}' passed. Please refer to https://aka.ms/CosmosClientEncryption for more details. "); } @@ -63,9 +63,7 @@ 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. "); - EncryptionKeyWrapProvider encryptionKeyWrapProvider = encryptionCosmosClient.EncryptionKeyWrapProvider; - - if (!string.Equals(encryptionKeyWrapMetadata.Type, encryptionKeyWrapProvider.ProviderName)) + if (!string.Equals(encryptionKeyWrapMetadata.Type, encryptionCosmosClient.KeyEncryptionKeyResolverName)) { 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. "); } @@ -73,7 +71,7 @@ public static async Task CreateClientEncryptionKeyA KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate( encryptionKeyWrapMetadata.Name, encryptionKeyWrapMetadata.Value, - encryptionKeyWrapProvider.EncryptionKeyStoreProviderImpl); + encryptionCosmosClient.EncryptionKeyStoreProviderImpl); ProtectedDataEncryptionKey protectedDataEncryptionKey = new ProtectedDataEncryptionKey( clientEncryptionKeyId, @@ -143,9 +141,7 @@ 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. "); - EncryptionKeyWrapProvider encryptionKeyWrapProvider = encryptionCosmosClient.EncryptionKeyWrapProvider; - - if (!string.Equals(newEncryptionKeyWrapMetadata.Type, encryptionKeyWrapProvider.ProviderName)) + if (!string.Equals(newEncryptionKeyWrapMetadata.Type, encryptionCosmosClient.KeyEncryptionKeyResolverName)) { 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. "); } @@ -160,14 +156,14 @@ public static async Task RewrapClientEncryptionKeyA KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate( clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Name, clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Value, - encryptionKeyWrapProvider.EncryptionKeyStoreProviderImpl); + encryptionCosmosClient.EncryptionKeyStoreProviderImpl); byte[] unwrappedKey = keyEncryptionKey.DecryptEncryptionKey(clientEncryptionKeyProperties.WrappedDataEncryptionKey); keyEncryptionKey = KeyEncryptionKey.GetOrCreate( newEncryptionKeyWrapMetadata.Name, newEncryptionKeyWrapMetadata.Value, - encryptionKeyWrapProvider.EncryptionKeyStoreProviderImpl); + encryptionCosmosClient.EncryptionKeyStoreProviderImpl); byte[] rewrappedKey = keyEncryptionKey.EncryptEncryptionKey(unwrappedKey); diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionKeyStoreProviderImpl.cs b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionKeyStoreProviderImpl.cs similarity index 61% rename from Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionKeyStoreProviderImpl.cs rename to Microsoft.Azure.Cosmos.Encryption/src/EncryptionKeyStoreProviderImpl.cs index 18ca46c344..5b23d37635 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionKeyStoreProviderImpl.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionKeyStoreProviderImpl.cs @@ -5,33 +5,36 @@ namespace Microsoft.Azure.Cosmos.Encryption { using System; + using global::Azure.Core.Cryptography; 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 + /// redirects all the corresponding calls to 's 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 + /// ProtectedDataEncryptionKey -> KeyEncryptionKey(containing EncryptionKeyStoreProviderImpl object) -> EncryptionKeyStoreProviderImpl.WrapKey -> this.keyEncryptionKeyResolver.WrapKey + /// ProtectedDataEncryptionKey -> KeyEncryptionKey(containing EncryptionKeyStoreProviderImpl object) -> EncryptionKeyStoreProviderImpl.UnWrapKey -> this.keyEncryptionKeyResolver.UnwrapKey /// /// internal class EncryptionKeyStoreProviderImpl : EncryptionKeyStoreProvider { - private readonly EncryptionKeyWrapProvider encryptionKeyWrapProvider; + private readonly IKeyEncryptionKeyResolver keyEncryptionKeyResolver; - public EncryptionKeyStoreProviderImpl(EncryptionKeyWrapProvider encryptionKeyWrapProvider) + public EncryptionKeyStoreProviderImpl(IKeyEncryptionKeyResolver keyEncryptionKeyResolver, string providerName) { - this.encryptionKeyWrapProvider = encryptionKeyWrapProvider; + this.keyEncryptionKeyResolver = keyEncryptionKeyResolver; + this.ProviderName = providerName; + this.DataEncryptionKeyCacheTimeToLive = TimeSpan.Zero; } - public override string ProviderName => this.encryptionKeyWrapProvider.ProviderName; + public override string ProviderName { get; } - public override byte[] UnwrapKey(string encryptionKeyId, Data.Encryption.Cryptography.KeyEncryptionKeyAlgorithm algorithm, byte[] encryptedKey) + public override byte[] UnwrapKey(string encryptionKeyId, 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. @@ -40,19 +43,27 @@ public override byte[] UnwrapKey(string encryptionKeyId, Data.Encryption.Cryptog // 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(); + return this.keyEncryptionKeyResolver + .Resolve(encryptionKeyId) + .UnwrapKey(EncryptionKeyStoreProviderImpl.GetNameForKeyEncryptionKeyAlgorithm(algorithm), encryptedKey); } } - public override byte[] WrapKey(string encryptionKeyId, Data.Encryption.Cryptography.KeyEncryptionKeyAlgorithm algorithm, byte[] key) + public override byte[] WrapKey(string encryptionKeyId, KeyEncryptionKeyAlgorithm algorithm, byte[] key) { - return this.encryptionKeyWrapProvider.WrapKeyAsync(encryptionKeyId, algorithm.ToString(), key) - .ConfigureAwait(false) - .GetAwaiter() - .GetResult(); + return this.keyEncryptionKeyResolver + .Resolve(encryptionKeyId) + .WrapKey(EncryptionKeyStoreProviderImpl.GetNameForKeyEncryptionKeyAlgorithm(algorithm), key); + } + + private static string GetNameForKeyEncryptionKeyAlgorithm(KeyEncryptionKeyAlgorithm algorithm) + { + if (algorithm == KeyEncryptionKeyAlgorithm.RSA_OAEP) + { + return "RSA-OAEP"; + } + + throw new InvalidOperationException(string.Format("Unexpected algorithm {0}", algorithm)); } /// @@ -60,7 +71,7 @@ public override byte[] WrapKey(string encryptionKeyId, Data.Encryption.Cryptogra /// public override byte[] Sign(string encryptionKeyId, bool allowEnclaveComputations) { - throw new NotSupportedException("The Sign operation is not supported. "); + throw new NotSupportedException("The Sign operation is not supported."); } /// @@ -68,7 +79,7 @@ public override byte[] Sign(string encryptionKeyId, bool allowEnclaveComputation /// public override bool Verify(string encryptionKeyId, bool allowEnclaveComputations, byte[] signature) { - throw new NotSupportedException("The Verify operation is not supported. "); + throw new NotSupportedException("The Verify operation is not supported."); } } } diff --git a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettingForProperty.cs b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettingForProperty.cs index bf7103faa2..582b92aa7c 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettingForProperty.cs +++ b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettingForProperty.cs @@ -50,7 +50,6 @@ 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.EncryptionKeyWrapProvider, this.ClientEncryptionKeyId, cancellationToken); } @@ -73,7 +72,6 @@ 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.EncryptionKeyWrapProvider, this.ClientEncryptionKeyId, cancellationToken); } @@ -141,7 +139,6 @@ private async Task ForceRefreshGatewayCacheAndBuildP ProtectedDataEncryptionKey protectedDataEncryptionKey = await this.BuildProtectedDataEncryptionKeyAsync( clientEncryptionKeyProperties, - this.encryptionContainer.EncryptionCosmosClient.EncryptionKeyWrapProvider, this.ClientEncryptionKeyId, cancellationToken); @@ -150,7 +147,6 @@ private async Task ForceRefreshGatewayCacheAndBuildP private async Task BuildProtectedDataEncryptionKeyAsync( ClientEncryptionKeyProperties clientEncryptionKeyProperties, - EncryptionKeyWrapProvider encryptionKeyWrapProvider, string keyId, CancellationToken cancellationToken) { @@ -161,7 +157,7 @@ private async Task BuildProtectedDataEncryptionKeyAs KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate( clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Name, clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Value, - encryptionKeyWrapProvider.EncryptionKeyStoreProviderImpl); + this.encryptionContainer.EncryptionCosmosClient.EncryptionKeyStoreProviderImpl); ProtectedDataEncryptionKey protectedDataEncryptionKey = ProtectedDataEncryptionKey.GetOrCreate( keyId, diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionType.cs b/Microsoft.Azure.Cosmos.Encryption/src/EncryptionType.cs similarity index 100% rename from Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionType.cs rename to Microsoft.Azure.Cosmos.Encryption/src/EncryptionType.cs diff --git a/Microsoft.Azure.Cosmos.Encryption/src/KeyEncryptionKeyResolverName.cs b/Microsoft.Azure.Cosmos.Encryption/src/KeyEncryptionKeyResolverName.cs new file mode 100644 index 0000000000..b37d050146 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/KeyEncryptionKeyResolverName.cs @@ -0,0 +1,19 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Encryption +{ + using global::Azure.Core.Cryptography; + + /// + /// Has constants for names of well-known implementations of . + /// + public static class KeyEncryptionKeyResolverName + { + /// + /// IKeyEncryptionKeyResolver implementation for keys in Azure Key Vault. + /// + public const string AzureKeyVault = "AZURE_KEY_VAULT"; + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/AzureKeyVaultKeyStoreProvider.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/AzureKeyVaultKeyStoreProvider.cs deleted file mode 100644 index 2c01f4fb17..0000000000 --- a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/AzureKeyVaultKeyStoreProvider.cs +++ /dev/null @@ -1,352 +0,0 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ - -// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. -// - -using System; -using System.Linq; -using System.Text; -using Azure.Core; -using Azure.Security.KeyVault.Keys.Cryptography; -using Microsoft.Data.Encryption.Cryptography; - -using static Microsoft.Data.Encryption.Resources.Strings; - -namespace 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. - /// - internal class AzureKeyVaultKeyStoreProvider : EncryptionKeyStoreProvider - { - #region Properties - - /// - /// Name of the Encryption Key Store Provider implemetation - /// - public override string ProviderName { get; } = "AZURE_KEY_VAULT"; - - /// - /// Key storage and cryptography client - /// - private KeyCryptographer KeyCryptographer { get; set; } - - /// - /// Algorithm version - /// - private readonly static byte[] firstVersion = new byte[] { 0x01 }; - - private readonly static KeyWrapAlgorithm keyWrapAlgorithm = KeyWrapAlgorithm.RsaOaep; - - /// - /// List of Trusted Endpoints - /// - public readonly string[] TrustedEndPoints; - - #endregion - - #region Constructors - /// - /// Constructor that takes an implementation of Token Credential that is capable of providing an OAuth Token. - /// - /// - public AzureKeyVaultKeyStoreProvider(TokenCredential tokenCredential) : - this(tokenCredential, Constants.AzureKeyVaultPublicDomainNames) - { } - - /// - /// Constructor that takes an implementation of Token Credential that is capable of providing an OAuth Token and a trusted endpoint. - /// - /// Instance of an implementation of Token Credential that is capable of providing an OAuth Token. - /// TrustedEndpoint is used to validate the key encryption key path. - public AzureKeyVaultKeyStoreProvider(TokenCredential tokenCredential, string trustedEndPoint) : - this(tokenCredential, new[] { trustedEndPoint }) - { } - - /// - /// Constructor that takes an instance of an implementation of Token Credential that is capable of providing an OAuth Token - /// and an array of trusted endpoints. - /// - /// Instance of an implementation of Token Credential that is capable of providing an OAuth Token - /// TrustedEndpoints are used to validate the key encryption key path - public AzureKeyVaultKeyStoreProvider(TokenCredential tokenCredential, string[] trustedEndPoints) - { - tokenCredential.ValidateNotNull(nameof(tokenCredential)); - trustedEndPoints.ValidateNotNull(nameof(trustedEndPoints)); - trustedEndPoints.ValidateNotEmpty(nameof(trustedEndPoints)); - trustedEndPoints.ValidateNotNullOrWhitespaceForEach(nameof(trustedEndPoints)); - - KeyCryptographer = new KeyCryptographer(tokenCredential); - TrustedEndPoints = trustedEndPoints; - } - #endregion - - #region Public methods - - /// - /// Uses an asymmetric key identified by the key path to sign the key encryption key metadata consisting of (keyEncryptionKeyPath, allowEnclaveComputations bit, providerName). - /// - /// Identifier of an asymmetric key in Azure Key Vault. - /// Indicates whether the key encryption key supports enclave computations. - /// The signature of the key encryption key metadata. - public override byte[] Sign(string encryptionKeyId, bool allowEnclaveComputations) - { - ValidateNonEmptyAKVPath(encryptionKeyId, isSystemOp: false); - - // Also validates key is of RSA type. - KeyCryptographer.AddKey(encryptionKeyId); - byte[] message = CompileKeyEncryptionKeyMetadata(encryptionKeyId, allowEnclaveComputations); - return KeyCryptographer.SignData(message, encryptionKeyId); - } - - /// - /// Uses an asymmetric key identified by the key path to verify the key encryption key metadata consisting of (keyEncryptionKeyPath, allowEnclaveComputations bit, providerName). - /// - /// Identifier of an asymmetric key in Azure Key Vault - /// Indicates whether the key encryption key supports enclave computations. - /// The signature of the key encryption key metadata. - /// Boolean indicating whether the key encryption key metadata can be verified based on the provided signature. - public override bool Verify(string encryptionKeyId, bool allowEnclaveComputations, byte[] signature) - { - ValidateNonEmptyAKVPath(encryptionKeyId, isSystemOp: true); - - var key = Tuple.Create(encryptionKeyId, allowEnclaveComputations, signature.ToHexString()); - return GetOrCreateSignatureVerificationResult(key, VerifyKeyEncryptionKeyMetadata); - - bool VerifyKeyEncryptionKeyMetadata() - { - // Also validates key is of RSA type. - KeyCryptographer.AddKey(encryptionKeyId); - byte[] message = CompileKeyEncryptionKeyMetadata(encryptionKeyId, allowEnclaveComputations); - return KeyCryptographer.VerifyData(message, signature, encryptionKeyId); - } - } - - /// - /// 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 encryption algorithm. - /// The ciphertext key. - /// Plain text data encryption key - public override byte[] UnwrapKey(string encryptionKeyId, KeyEncryptionKeyAlgorithm algorithm, byte[] encryptedKey) - { - // Validate the input parameters - ValidateNonEmptyAKVPath(encryptionKeyId, isSystemOp: true); - ValidateEncryptionAlgorithm(algorithm); - encryptedKey.ValidateNotNull(nameof(encryptedKey)); - encryptedKey.ValidateNotEmpty(nameof(encryptedKey)); - ValidateVersionByte(encryptedKey[0], firstVersion[0]); - - return GetOrCreateDataEncryptionKey(encryptedKey.ToHexString(), DecryptEncryptionKey); - - byte[] DecryptEncryptionKey() - { - // Also validates whether the key is RSA one or not and then get the key size - KeyCryptographer.AddKey(encryptionKeyId); - - int keySizeInBytes = KeyCryptographer.GetKeySize(encryptionKeyId); - - // Get key path length - int currentIndex = firstVersion.Length; - ushort keyPathLength = BitConverter.ToUInt16(encryptedKey, currentIndex); - currentIndex += sizeof(ushort); - - // Get ciphertext length - ushort cipherTextLength = BitConverter.ToUInt16(encryptedKey, currentIndex); - currentIndex += sizeof(ushort); - - // Skip KeyPath - // KeyPath exists only for troubleshooting purposes and doesnt need validation. - currentIndex += keyPathLength; - - // validate the ciphertext length - if (cipherTextLength != keySizeInBytes) - { - throw new MicrosoftDataEncryptionException(InvalidCiphertextLengthTemplate.FormatInvariant(cipherTextLength, keySizeInBytes, encryptionKeyId)); - } - - // Validate the signature length - int signatureLength = encryptedKey.Length - currentIndex - cipherTextLength; - if (signatureLength != keySizeInBytes) - { - throw new MicrosoftDataEncryptionException(InvalidSignatureLengthTemplate.FormatInvariant(signatureLength, keySizeInBytes, encryptionKeyId)); - } - - // Get ciphertext - byte[] cipherText = encryptedKey.Skip(currentIndex).Take(cipherTextLength).ToArray(); - currentIndex += cipherTextLength; - - // Get signature - byte[] signature = encryptedKey.Skip(currentIndex).Take(signatureLength).ToArray(); - - // Compute the message to validate the signature - byte[] message = encryptedKey.Take(encryptedKey.Length - signatureLength).ToArray(); - - if (null == message) - { - throw new MicrosoftDataEncryptionException(NullHash); - } - - if (!KeyCryptographer.VerifyData(message, signature, encryptionKeyId)) - { - throw new MicrosoftDataEncryptionException(InvalidSignatureTemplate.FormatInvariant(encryptionKeyId)); - } - - return KeyCryptographer.UnwrapKey(keyWrapAlgorithm, cipherText, encryptionKeyId); - } - } - - /// - /// 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 encryption algorithm. - /// The plaintext key. - /// Encrypted data encryption key - public override byte[] WrapKey(string encryptionKeyId, KeyEncryptionKeyAlgorithm algorithm, byte[] key) - { - // Validate the input parameters - ValidateNonEmptyAKVPath(encryptionKeyId, isSystemOp: true); - ValidateEncryptionAlgorithm(algorithm); - key.ValidateNotNull(nameof(key)); - ValidateDataEncryptionKeyNotEmpty(key); - - // Also validates whether the key is RSA one or not and then get the key size - KeyCryptographer.AddKey(encryptionKeyId); - int keySizeInBytes = KeyCryptographer.GetKeySize(encryptionKeyId); - - // Construct the encryptedDataEncryptionKey - // Format is - // firstVersion + keyPathLength + ciphertextLength + keyPath + ciphertext + signature - - // Get the Unicode encoded bytes of cultureinvariant lower case keyEncryptionKeyPath - byte[] keyEncryptionKeyPathBytes = Encoding.Unicode.GetBytes(encryptionKeyId.ToLowerInvariant()); - byte[] keyPathLength = BitConverter.GetBytes((short)keyEncryptionKeyPathBytes.Length); - - // Encrypt the plain text - byte[] cipherText = KeyCryptographer.WrapKey(keyWrapAlgorithm, key, encryptionKeyId); - byte[] cipherTextLength = BitConverter.GetBytes((short)cipherText.Length); - - if (cipherText.Length != keySizeInBytes) - { - throw new MicrosoftDataEncryptionException(CipherTextLengthMismatch); - } - - // Compute message - // SHA-2-256(version + keyPathLength + ciphertextLength + keyPath + ciphertext) - byte[] message = firstVersion.Concat(keyPathLength).Concat(cipherTextLength).Concat(keyEncryptionKeyPathBytes).Concat(cipherText).ToArray(); - - // Sign the message - byte[] signature = KeyCryptographer.SignData(message, encryptionKeyId); - - if (signature.Length != keySizeInBytes) - { - throw new MicrosoftDataEncryptionException(HashLengthMismatch); - } - - ValidateSignature(encryptionKeyId, message, signature); - - return message.Concat(signature).ToArray(); - } - - #endregion - - #region Private methods - - private void ValidateDataEncryptionKeyNotEmpty(byte[] encryptionKey) - { - if (encryptionKey.Length == 0) - { - throw new MicrosoftDataEncryptionException(EmptyDataEncryptionKey); - } - } - - /// - /// Checks if the Azure Key Vault key path is Empty or Null (and raises exception if they are). - /// - internal void ValidateNonEmptyAKVPath(string keyEncryptionKeyPath, bool isSystemOp) - { - // throw appropriate error if keyEncryptionKeyPath is null or empty - if (string.IsNullOrWhiteSpace(keyEncryptionKeyPath)) - { - string errorMessage = null == keyEncryptionKeyPath - ? NullAkvPath - : InvalidAkvPathTemplate.FormatInvariant(keyEncryptionKeyPath); - - if (isSystemOp) - { - throw new MicrosoftDataEncryptionException(errorMessage); - } - - throw new MicrosoftDataEncryptionException(errorMessage); - } - - - if (!Uri.TryCreate(keyEncryptionKeyPath, UriKind.Absolute, out Uri parsedUri) || parsedUri.Segments.Length < 3) - { - // Return an error indicating that the AKV url is invalid. - throw new MicrosoftDataEncryptionException(InvalidAkvUrlTemplate.FormatInvariant(keyEncryptionKeyPath)); - } - - // A valid URI. - // Check if it is pointing to trusted endpoint. - foreach (string trustedEndPoint in TrustedEndPoints) - { - if (parsedUri.Host.EndsWith(trustedEndPoint, StringComparison.OrdinalIgnoreCase)) - { - return; - } - } - - // Return an error indicating that the AKV url is invalid. - throw new MicrosoftDataEncryptionException(InvalidAkvKeyPathTrustedTemplate.FormatInvariant(keyEncryptionKeyPath, string.Join(", ", TrustedEndPoints.ToArray()))); - } - - private void ValidateSignature(string keyEncryptionKeyPath, byte[] message, byte[] signature) - { - if (!KeyCryptographer.VerifyData(message, signature, keyEncryptionKeyPath)) - { - throw new MicrosoftDataEncryptionException(InvalidSignature); - } - } - - private byte[] CompileKeyEncryptionKeyMetadata(string keyEncryptionKeyPath, bool allowEnclaveComputations) - { - string keyEncryptionKeyMetadata = ProviderName + keyEncryptionKeyPath + allowEnclaveComputations; - return Encoding.Unicode.GetBytes(keyEncryptionKeyMetadata.ToLowerInvariant()); - } - - - internal static void ValidateEncryptionAlgorithm(KeyEncryptionKeyAlgorithm encryptionAlgorithm) - { - if (encryptionAlgorithm != KeyEncryptionKeyAlgorithm.RSA_OAEP) - { - throw new MicrosoftDataEncryptionException(InvalidKeyAlgorithm.FormatInvariant(encryptionAlgorithm, KeyEncryptionKeyAlgorithm.RSA_OAEP.ToString())); - } - } - - internal static void ValidateVersionByte(byte encryptedByte, byte firstVersionByte) - { - // Validate and decrypt the EncryptedDataEncryptionKey - // Format is - // version + keyPathLength + ciphertextLength + keyPath + ciphertext + signature - // - // keyPath is present in the encrypted data encryption key for identifying the original source of the asymmetric key pair and - // we will not validate it against the data contained in the KEK metadata (keyEncryptionKeyPath). - - // Validate the version byte - if (encryptedByte != firstVersionByte) - { - throw new MicrosoftDataEncryptionException(InvalidAlgorithmVersionTemplate.FormatInvariant(encryptedByte.ToString(@"X2"), firstVersionByte.ToString("X2"))); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/Constants.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/Constants.cs deleted file mode 100644 index b8db49e2a2..0000000000 --- a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/Constants.cs +++ /dev/null @@ -1,26 +0,0 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ - -// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. -// - -namespace Microsoft.Data.Encryption.AzureKeyVaultProvider -{ - internal static class Constants - { - /// - /// Azure Key Vault Domain Name - /// - internal static readonly string[] AzureKeyVaultPublicDomainNames = new string[] { - @"vault.azure.net", // default - @"vault.azure.cn", // Azure China - @"vault.usgovcloudapi.net", // US Government - @"vault.microsoftazure.de", // Azure Germany - @"managedhsm.azure.net", // public HSM vault - @"managedhsm.azure.cn", // Azure China HSM vault - @"managedhsm.usgovcloudapi.net", // US Government HSM vault - @"managedhsm.microsoftazure.de" // Azure Germany HSM vault - }; - } -} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/KeyCryptographer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/KeyCryptographer.cs deleted file mode 100644 index 5e065861ee..0000000000 --- a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/KeyCryptographer.cs +++ /dev/null @@ -1,226 +0,0 @@ -//------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -//------------------------------------------------------------ - -// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. -// - -using Azure; -using Azure.Core; -using Azure.Security.KeyVault.Keys; -using Azure.Security.KeyVault.Keys.Cryptography; -using System; -using System.Collections.Concurrent; -using System.Threading.Tasks; - -using static Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm; -using static Microsoft.Data.Encryption.Resources.Strings; - -namespace Microsoft.Data.Encryption.AzureKeyVaultProvider -{ - internal class KeyCryptographer - { - /// - /// TokenCredential to be used with the KeyClient - /// - private TokenCredential TokenCredential { get; set; } - - /// - /// A mapping of the KeyClient objects to the corresponding Azure Key Vault URI - /// - private readonly ConcurrentDictionary _keyClientDictionary = new ConcurrentDictionary(); - - /// - /// Holds references to the fetch key tasks and maps them to their corresponding Azure Key Vault Key Identifier (URI). - /// These tasks will be used for returning the key in the event that the fetch task has not finished depositing the - /// key into the key dictionary. - /// - private readonly ConcurrentDictionary>> _keyFetchTaskDictionary = new ConcurrentDictionary>>(); - - /// - /// Holds references to the Azure Key Vault keys and maps them to their corresponding Azure Key Vault Key Identifier (URI). - /// - private readonly ConcurrentDictionary _keyDictionary = new ConcurrentDictionary(); - - /// - /// Holds references to the Azure Key Vault CryptographyClient objects and maps them to their corresponding Azure Key Vault Key Identifier (URI). - /// - private readonly ConcurrentDictionary _cryptoClientDictionary = new ConcurrentDictionary(); - - /// - /// Constructs a new KeyCryptographer - /// - /// - internal KeyCryptographer(TokenCredential tokenCredential) - { - TokenCredential = tokenCredential; - } - - /// - /// Adds the key, specified by the Key Identifier URI, to the cache. - /// - /// - internal void AddKey(string keyIdentifierUri) - { - if (TheKeyHasNotBeenCached(keyIdentifierUri)) - { - ParseAKVPath(keyIdentifierUri, out Uri vaultUri, out string keyName, out string keyVersion); - CreateKeyClient(vaultUri); - FetchKey(vaultUri, keyName, keyVersion, keyIdentifierUri); - } - - bool TheKeyHasNotBeenCached(string k) => !_keyDictionary.ContainsKey(k) && !_keyFetchTaskDictionary.ContainsKey(k); - } - - /// - /// Returns the key specified by the Key Identifier URI - /// - /// - /// - internal KeyVaultKey GetKey(string keyIdentifierUri) - { - if (_keyDictionary.ContainsKey(keyIdentifierUri)) - { - _keyDictionary.TryGetValue(keyIdentifierUri, out KeyVaultKey key); - return key; - } - - if (_keyFetchTaskDictionary.ContainsKey(keyIdentifierUri)) - { - _keyFetchTaskDictionary.TryGetValue(keyIdentifierUri, out Task> task); - return Task.Run(() => task).GetAwaiter().GetResult(); - } - - // Not a public exception - not likely to occur. - throw new MicrosoftDataEncryptionException(AzureKeyVaultKeyNotFound.Format(keyIdentifierUri)); - } - - /// - /// Gets the public Key size in bytes. - /// - /// The key vault key identifier URI - /// - internal int GetKeySize(string keyIdentifierUri) - { - return GetKey(keyIdentifierUri).Key.N.Length; - } - - /// - /// Generates signature based on RSA PKCS#v1.5 scheme using a specified Azure Key Vault Key URL. - /// - /// The data to sign - /// The key vault key identifier URI - /// - internal byte[] SignData(byte[] message, string keyIdentifierUri) - { - CryptographyClient cryptographyClient = GetCryptographyClient(keyIdentifierUri); - return cryptographyClient.SignData(RS256, message).Signature; - } - - internal bool VerifyData(byte[] message, byte[] signature, string keyIdentifierUri) - { - CryptographyClient cryptographyClient = GetCryptographyClient(keyIdentifierUri); - return cryptographyClient.VerifyData(RS256, message, signature).IsValid; - } - - internal byte[] UnwrapKey(KeyWrapAlgorithm keyWrapAlgorithm, byte[] encryptedKey, string keyIdentifierUri) - { - CryptographyClient cryptographyClient = GetCryptographyClient(keyIdentifierUri); - return cryptographyClient.UnwrapKey(keyWrapAlgorithm, encryptedKey).Key; - } - - internal byte[] WrapKey(KeyWrapAlgorithm keyWrapAlgorithm, byte[] key, string keyIdentifierUri) - { - CryptographyClient cryptographyClient = GetCryptographyClient(keyIdentifierUri); - return cryptographyClient.WrapKey(keyWrapAlgorithm, key).EncryptedKey; - } - - private CryptographyClient GetCryptographyClient(string keyIdentifierUri) - { - if (_cryptoClientDictionary.ContainsKey(keyIdentifierUri)) - { - _cryptoClientDictionary.TryGetValue(keyIdentifierUri, out CryptographyClient client); - return client; - } - - CryptographyClient cryptographyClient = new CryptographyClient(GetKey(keyIdentifierUri).Id, TokenCredential); - _cryptoClientDictionary.TryAdd(keyIdentifierUri, cryptographyClient); - - return cryptographyClient; - } - - /// - /// - /// - /// The Azure Key Vault URI - /// The name of the Azure Key Vault key - /// The version of the Azure Key Vault key - /// The Azure Key Vault key identifier - private void FetchKey(Uri vaultUri, string keyName, string keyVersion, string keyResourceUri) - { - Task> fetchKeyTask = FetchKeyFromKeyVault(vaultUri, keyName, keyVersion); - _keyFetchTaskDictionary.AddOrUpdate(keyResourceUri, fetchKeyTask, (k, v) => fetchKeyTask); - - fetchKeyTask - .ContinueWith(k => ValidateRsaKey(k.GetAwaiter().GetResult())) - .ContinueWith(k => _keyDictionary.AddOrUpdate(keyResourceUri, k.GetAwaiter().GetResult(), (key, v) => k.GetAwaiter().GetResult())); - - Task.Run(() => fetchKeyTask); - } - - /// - /// Looks up the KeyClient object by it's URI and then fetches the key by name. - /// - /// The Azure Key Vault URI - /// Then name of the key - /// Then version of the key - /// - private Task> FetchKeyFromKeyVault(Uri vaultUri, string keyName, string keyVersion) - { - _keyClientDictionary.TryGetValue(vaultUri, out KeyClient keyClient); - return keyClient.GetKeyAsync(keyName, keyVersion); - } - - /// - /// Validates that a key is of type RSA - /// - /// - /// - private KeyVaultKey ValidateRsaKey(KeyVaultKey key) - { - if (key.KeyType != KeyType.Rsa && key.KeyType != KeyType.RsaHsm) - { - throw new MicrosoftDataEncryptionException(NonRsaKeyTemplate.Format(key.KeyType)); - } - - return key; - } - - /// - /// Instantiates and adds a KeyClient to the KeyClient dictionary - /// - /// The Azure Key Vault URI - private void CreateKeyClient(Uri vaultUri) - { - if (!_keyClientDictionary.ContainsKey(vaultUri)) - { - _keyClientDictionary.TryAdd(vaultUri, new KeyClient(vaultUri, TokenCredential)); - } - } - - /// - /// Validates and parses the Azure Key Vault URI and key name. - /// - /// The Azure Key Vault key identifier - /// The Azure Key Vault URI - /// The name of the key - /// The version of the key - private void ParseAKVPath(string keyEncryptionKeyPath, out Uri vaultUri, out string keyEncryptionKeyName, out string keyEncryptionKeyVersion) - { - Uri masterKeyPathUri = new Uri(keyEncryptionKeyPath); - vaultUri = new Uri(masterKeyPathUri.GetLeftPart(UriPartial.Authority)); - keyEncryptionKeyName = masterKeyPathUri.Segments[2]; - keyEncryptionKeyVersion = masterKeyPathUri.Segments.Length > 3 ? masterKeyPathUri.Segments[3] : null; - } - } -} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/AzureKeyVaultKeyWrapProvider.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/AzureKeyVaultKeyWrapProvider.cs deleted file mode 100644 index 3945d380c1..0000000000 --- a/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/AzureKeyVaultKeyWrapProvider.cs +++ /dev/null @@ -1,77 +0,0 @@ -//------------------------------------------------------------ -// 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/EncryptionKeyWrapProvider.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionKeyWrapProvider.cs deleted file mode 100644 index 408f060a7c..0000000000 --- a/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/EncryptionKeyWrapProvider.cs +++ /dev/null @@ -1,92 +0,0 @@ -//------------------------------------------------------------ -// 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/MdeSupport/KeyEncryptionKeyAlgorithm.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/KeyEncryptionKeyAlgorithm.cs deleted file mode 100644 index 255000884f..0000000000 --- a/Microsoft.Azure.Cosmos.Encryption/src/MdeSupport/KeyEncryptionKeyAlgorithm.cs +++ /dev/null @@ -1,17 +0,0 @@ -//------------------------------------------------------------ -// 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 11c8344f7d..aa72dc9d65 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/Microsoft.Azure.Cosmos.Encryption.csproj +++ b/Microsoft.Azure.Cosmos.Encryption/src/Microsoft.Azure.Cosmos.Encryption.csproj @@ -34,7 +34,6 @@ - diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs b/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs index 071f704bc2..2368e8e71d 100644 --- a/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs +++ b/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs @@ -15,6 +15,7 @@ namespace Microsoft.Azure.Cosmos.Encryption.EmulatorTests using System.Threading; using System.Threading.Tasks; using global::Azure; + using global::Azure.Core.Cryptography; using Microsoft.Azure.Cosmos; using Microsoft.Azure.Cosmos.Encryption; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -33,22 +34,23 @@ public class MdeEncryptionTests private static Database database; private static Container encryptionContainer; private static Container encryptionContainerForChangeFeed; - private static TestEncryptionKeyWrapProvider testEncryptionKeyWrapProvider; + private static TestKeyEncryptionKeyResolver testKeyEncryptionKeyResolver; [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(); - testEncryptionKeyWrapProvider = new TestEncryptionKeyWrapProvider - { - DataEncryptionKeyCacheTimeToLive = null - }; + testKeyEncryptionKeyResolver = new TestKeyEncryptionKeyResolver(); + + metadata1 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(TestKeyEncryptionKeyResolver.Id, "key1", "tempmetadata1"); + metadata2 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(TestKeyEncryptionKeyResolver.Id, "key2", "tempmetadata2"); - metadata1 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, "key1", "tempmetadata1"); - metadata2 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, "key2", "tempmetadata2"); + MdeEncryptionTests.encryptionCosmosClient = MdeEncryptionTests.client.WithEncryption( + testKeyEncryptionKeyResolver, + TestKeyEncryptionKeyResolver.Id, + TimeSpan.Zero); - MdeEncryptionTests.encryptionCosmosClient = MdeEncryptionTests.client.WithEncryption(testEncryptionKeyWrapProvider); MdeEncryptionTests.database = await MdeEncryptionTests.encryptionCosmosClient.CreateDatabaseAsync(Guid.NewGuid().ToString()); await MdeEncryptionTests.CreateClientEncryptionKeyAsync( @@ -60,10 +62,11 @@ await MdeEncryptionTests.CreateClientEncryptionKeyAsync( metadata2); - EncryptionKeyWrapMetadata revokedKekmetadata = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, "revokedKek", "revokedKek-metadata"); + EncryptionKeyWrapMetadata revokedKekmetadata = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(TestKeyEncryptionKeyResolver.Id, "revokedKek", "revokedKek-metadata"); + await database.CreateClientEncryptionKeyAsync( "keywithRevokedKek", - DataEncryptionKeyAlgorithm.AeadAes256CbcHmacSha256, + EncryptionAlgorithm.AeadAes256CbcHmacSha256, revokedKekmetadata); Collection paths = new Collection() @@ -179,11 +182,18 @@ await database.CreateClientEncryptionKeyAsync( await encryptionContainerForChangeFeed.InitializeEncryptionAsync(); } + [TestInitialize] + public void TestInitialize() + { + // Reset static cache TTL + Microsoft.Data.Encryption.Cryptography.ProtectedDataEncryptionKey.TimeToLive = TimeSpan.FromHours(2); + } + private static async Task CreateClientEncryptionKeyAsync(string cekId, Cosmos.EncryptionKeyWrapMetadata encryptionKeyWrapMetadata) { ClientEncryptionKeyResponse clientEncrytionKeyResponse = await database.CreateClientEncryptionKeyAsync( cekId, - DataEncryptionKeyAlgorithm.AeadAes256CbcHmacSha256, + EncryptionAlgorithm.AeadAes256CbcHmacSha256, encryptionKeyWrapMetadata); Assert.AreEqual(HttpStatusCode.Created, clientEncrytionKeyResponse.StatusCode); @@ -235,7 +245,7 @@ public async Task EncryptionBulkCrud() .WithBulkExecution(true) .Build()); - CosmosClient encryptionCosmosClientWithBulk = clientWithBulk.WithEncryption(new TestEncryptionKeyWrapProvider()); + CosmosClient encryptionCosmosClientWithBulk = clientWithBulk.WithEncryption(new TestKeyEncryptionKeyResolver(), TestKeyEncryptionKeyResolver.Id); Database databaseWithBulk = encryptionCosmosClientWithBulk.GetDatabase(MdeEncryptionTests.database.Id); Container encryptionContainerWithBulk = databaseWithBulk.GetContainer(MdeEncryptionTests.encryptionContainer.Id); @@ -257,18 +267,18 @@ public async Task EncryptionCreateClientEncryptionKey() { string cekId = "anotherCek"; - EncryptionKeyWrapMetadata metadata1 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata1"); + EncryptionKeyWrapMetadata metadata1 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(TestKeyEncryptionKeyResolver.Id, cekId, "testmetadata1"); ClientEncryptionKeyProperties clientEncryptionKeyProperties = await MdeEncryptionTests.CreateClientEncryptionKeyAsync( cekId, metadata1); Assert.AreEqual( - MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, name: cekId, value: metadata1.Value), + MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(TestKeyEncryptionKeyResolver.Id, name: cekId, value: metadata1.Value), clientEncryptionKeyProperties.EncryptionKeyWrapMetadata); // creating another key with same id should fail - metadata1 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata2"); + metadata1 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(TestKeyEncryptionKeyResolver.Id, cekId, "testmetadata2"); try { @@ -289,22 +299,22 @@ await MdeEncryptionTests.CreateClientEncryptionKeyAsync( public async Task EncryptionRewrapClientEncryptionKey() { string cekId = "rewrapkeytest"; - EncryptionKeyWrapMetadata metadata1 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata1"); + EncryptionKeyWrapMetadata metadata1 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(TestKeyEncryptionKeyResolver.Id, cekId, "testmetadata1"); ClientEncryptionKeyProperties clientEncryptionKeyProperties = await MdeEncryptionTests.CreateClientEncryptionKeyAsync( cekId, metadata1); Assert.AreEqual( - MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, name: cekId, value: metadata1.Value), + MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(TestKeyEncryptionKeyResolver.Id, name: cekId, value: metadata1.Value), clientEncryptionKeyProperties.EncryptionKeyWrapMetadata); - EncryptionKeyWrapMetadata updatedMetaData = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, metadata1 + "updatedmetadata"); + EncryptionKeyWrapMetadata updatedMetaData = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(TestKeyEncryptionKeyResolver.Id, cekId, metadata1 + "updatedmetadata"); clientEncryptionKeyProperties = await MdeEncryptionTests.RewarpClientEncryptionKeyAsync( cekId, updatedMetaData); Assert.AreEqual( - MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, name: cekId, value: updatedMetaData.Value), + MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(TestKeyEncryptionKeyResolver.Id, name: cekId, value: updatedMetaData.Value), clientEncryptionKeyProperties.EncryptionKeyWrapMetadata); } @@ -332,12 +342,9 @@ public async Task EncryptionCreateItemWithNullProperty() CosmosClient clientWithNoCaching = TestCommon.CreateCosmosClient(builder => builder .Build()); - TestEncryptionKeyWrapProvider testEncryptionKeyWrapProvider = new TestEncryptionKeyWrapProvider - { - DataEncryptionKeyCacheTimeToLive = TimeSpan.Zero - }; + TestKeyEncryptionKeyResolver testKeyEncryptionKeyResolver = new TestKeyEncryptionKeyResolver(); - CosmosClient encryptionCosmosClient = clientWithNoCaching.WithEncryption(testEncryptionKeyWrapProvider); + CosmosClient encryptionCosmosClient = clientWithNoCaching.WithEncryption(testKeyEncryptionKeyResolver, TestKeyEncryptionKeyResolver.Id, TimeSpan.Zero); Database database = encryptionCosmosClient.GetDatabase(MdeEncryptionTests.database.Id); Container encryptionContainer = database.GetContainer(MdeEncryptionTests.encryptionContainer.Id); @@ -395,7 +402,7 @@ await MdeEncryptionTests.ValidateQueryResultsAsync( expectedDoc: expectedDoc); // no access to key. - testEncryptionKeyWrapProvider.RevokeAccessSet = true; + testKeyEncryptionKeyResolver.RevokeAccessSet = true; testDoc = TestDoc.Create(); @@ -409,7 +416,7 @@ await MdeEncryptionTests.ValidateQueryResultsAsync( Assert.AreEqual(HttpStatusCode.Created, createResponse.StatusCode); VerifyExpectedDocResponse(testDoc, createResponse.Resource); - testEncryptionKeyWrapProvider.RevokeAccessSet = false; + testKeyEncryptionKeyResolver.RevokeAccessSet = false; } @@ -428,18 +435,18 @@ public async Task EncryptionResourceTokenAuthRestricted() restrictedUserPermission.Token); - CosmosClient encryptedclientForRestrictedUser = clientForRestrictedUser.WithEncryption(new TestEncryptionKeyWrapProvider()); + CosmosClient encryptedclientForRestrictedUser = clientForRestrictedUser.WithEncryption(new TestKeyEncryptionKeyResolver(), TestKeyEncryptionKeyResolver.Id); Database databaseForRestrictedUser = encryptedclientForRestrictedUser.GetDatabase(MdeEncryptionTests.database.Id); try { string cekId = "testingcekID"; - EncryptionKeyWrapMetadata metadata1 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata1"); + EncryptionKeyWrapMetadata metadata1 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(TestKeyEncryptionKeyResolver.Id, cekId, "testmetadata1"); ClientEncryptionKeyResponse clientEncrytionKeyResponse = await databaseForRestrictedUser.CreateClientEncryptionKeyAsync( cekId, - DataEncryptionKeyAlgorithm.AeadAes256CbcHmacSha256, + EncryptionAlgorithm.AeadAes256CbcHmacSha256, metadata1); Assert.Fail("CreateClientEncryptionKeyAsync should have failed due to restrictions"); } @@ -450,7 +457,7 @@ public async Task EncryptionResourceTokenAuthRestricted() try { string cekId = "testingcekID"; - EncryptionKeyWrapMetadata metadata1 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata1" + "updated"); + EncryptionKeyWrapMetadata metadata1 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(TestKeyEncryptionKeyResolver.Id, cekId, "testmetadata1" + "updated"); ClientEncryptionKeyResponse clientEncrytionKeyResponse = await databaseForRestrictedUser.RewrapClientEncryptionKeyAsync( cekId, @@ -1001,7 +1008,7 @@ public async Task EncryptionValidatePolicyRefreshPostContainerDeleteWithBulk() .WithBulkExecution(false) .Build()); - CosmosClient otherEncryptionClient = otherClient.WithEncryption(new TestEncryptionKeyWrapProvider()); + CosmosClient otherEncryptionClient = otherClient.WithEncryption(new TestKeyEncryptionKeyResolver(), TestKeyEncryptionKeyResolver.Id); Database otherDatabase = otherEncryptionClient.GetDatabase(MdeEncryptionTests.database.Id); Container otherEncryptionContainer = otherDatabase.GetContainer(encryptionContainerToDelete.Id); @@ -1150,7 +1157,7 @@ public async Task EncryptionValidatePolicyRefreshPostContainerDeleteTransactionB CosmosClient otherClient = TestCommon.CreateCosmosClient(builder => builder .Build()); - CosmosClient otherEncryptionClient = otherClient.WithEncryption(new TestEncryptionKeyWrapProvider()); + CosmosClient otherEncryptionClient = otherClient.WithEncryption(new TestKeyEncryptionKeyResolver(), TestKeyEncryptionKeyResolver.Id); Database otherDatabase = otherEncryptionClient.GetDatabase(MdeEncryptionTests.database.Id); Container otherEncryptionContainer = otherDatabase.GetContainer(encryptionContainerToDelete.Id); @@ -1282,7 +1289,7 @@ public async Task EncryptionValidatePolicyRefreshPostContainerDeleteQuery() CosmosClient otherClient = TestCommon.CreateCosmosClient(builder => builder .Build()); - CosmosClient otherEncryptionClient = otherClient.WithEncryption(new TestEncryptionKeyWrapProvider()); + CosmosClient otherEncryptionClient = otherClient.WithEncryption(new TestKeyEncryptionKeyResolver(), TestKeyEncryptionKeyResolver.Id); Database otherDatabase = otherEncryptionClient.GetDatabase(MdeEncryptionTests.database.Id); Container otherEncryptionContainer = otherDatabase.GetContainer(encryptionContainerToDelete.Id); @@ -1397,7 +1404,7 @@ public async Task EncryptionValidatePolicyRefreshPostContainerDeletePatch() CosmosClient otherClient = TestCommon.CreateCosmosClient(builder => builder .Build()); - CosmosClient otherEncryptionClient = otherClient.WithEncryption(new TestEncryptionKeyWrapProvider()); + CosmosClient otherEncryptionClient = otherClient.WithEncryption(new TestKeyEncryptionKeyResolver(), TestKeyEncryptionKeyResolver.Id); Database otherDatabase = otherEncryptionClient.GetDatabase(MdeEncryptionTests.database.Id); Container otherEncryptionContainer = otherDatabase.GetContainer(encryptionContainerToDelete.Id); @@ -1544,18 +1551,15 @@ public async Task EncryptionValidatePolicyRefreshPostDatabaseDelete() CosmosClient mainClient = TestCommon.CreateCosmosClient(builder => builder .Build()); - TestEncryptionKeyWrapProvider testEncryptionKeyWrapProvider = new TestEncryptionKeyWrapProvider - { - DataEncryptionKeyCacheTimeToLive = TimeSpan.FromMinutes(30), - }; + TestKeyEncryptionKeyResolver testKeyEncryptionKeyResolver = new TestKeyEncryptionKeyResolver(); - EncryptionKeyWrapMetadata keyWrapMetadata = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, "myCek", "mymetadata1"); - CosmosClient encryptionCosmosClient = mainClient.WithEncryption(testEncryptionKeyWrapProvider); + EncryptionKeyWrapMetadata keyWrapMetadata = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(TestKeyEncryptionKeyResolver.Id, "myCek", "mymetadata1"); + CosmosClient encryptionCosmosClient = mainClient.WithEncryption(testKeyEncryptionKeyResolver, TestKeyEncryptionKeyResolver.Id, TimeSpan.FromMinutes(30)); Database mainDatabase = await encryptionCosmosClient.CreateDatabaseAsync("databaseToBeDeleted"); ClientEncryptionKeyResponse clientEncrytionKeyResponse = await mainDatabase.CreateClientEncryptionKeyAsync( keyWrapMetadata.Name, - DataEncryptionKeyAlgorithm.AeadAes256CbcHmacSha256, + EncryptionAlgorithm.AeadAes256CbcHmacSha256, keyWrapMetadata); Collection originalPaths = new Collection() @@ -1597,12 +1601,9 @@ public async Task EncryptionValidatePolicyRefreshPostDatabaseDelete() CosmosClient otherClient1 = TestCommon.CreateCosmosClient(builder => builder .Build()); - TestEncryptionKeyWrapProvider testEncryptionKeyWrapProvider2 = new TestEncryptionKeyWrapProvider - { - DataEncryptionKeyCacheTimeToLive = TimeSpan.Zero, - }; + TestKeyEncryptionKeyResolver testKeyEncryptionKeyResolver2 = new TestKeyEncryptionKeyResolver(); - CosmosClient otherEncryptionClient = otherClient1.WithEncryption(testEncryptionKeyWrapProvider2); + CosmosClient otherEncryptionClient = otherClient1.WithEncryption(testKeyEncryptionKeyResolver2, TestKeyEncryptionKeyResolver.Id, TimeSpan.Zero); Database otherDatabase = otherEncryptionClient.GetDatabase(mainDatabase.Id); Container otherEncryptionContainer = otherDatabase.GetContainer(encryptionContainerToDelete.Id); @@ -1615,10 +1616,10 @@ public async Task EncryptionValidatePolicyRefreshPostDatabaseDelete() mainDatabase = await encryptionCosmosClient.CreateDatabaseAsync("databaseToBeDeleted"); - keyWrapMetadata = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider2.ProviderName, "myCek", "mymetadata2"); + keyWrapMetadata = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(TestKeyEncryptionKeyResolver.Id, "myCek", "mymetadata2"); clientEncrytionKeyResponse = await mainDatabase.CreateClientEncryptionKeyAsync( keyWrapMetadata.Name, - DataEncryptionKeyAlgorithm.AeadAes256CbcHmacSha256, + EncryptionAlgorithm.AeadAes256CbcHmacSha256, keyWrapMetadata); using (await mainDatabase.GetContainer(encryptionContainerToDelete.Id).DeleteContainerStreamAsync()) @@ -1723,12 +1724,9 @@ public async Task EncryptionValidatePolicyRefreshPostDatabaseDelete() .WithBulkExecution(true) .Build()); - TestEncryptionKeyWrapProvider testEncryptionKeyWrapProvider3 = new TestEncryptionKeyWrapProvider - { - DataEncryptionKeyCacheTimeToLive = TimeSpan.FromMinutes(30), - }; + TestKeyEncryptionKeyResolver testKeyEncryptionKeyResolver3 = new TestKeyEncryptionKeyResolver(); - CosmosClient otherEncryptionClient2 = otherClient2.WithEncryption(testEncryptionKeyWrapProvider3); + CosmosClient otherEncryptionClient2 = otherClient2.WithEncryption(testKeyEncryptionKeyResolver3, TestKeyEncryptionKeyResolver.Id, TimeSpan.FromMinutes(30)); Database otherDatabase2 = otherEncryptionClient2.GetDatabase(mainDatabase.Id); Container otherEncryptionContainer3 = otherDatabase2.GetContainer(otherEncryptionContainer2.Id); @@ -1808,68 +1806,15 @@ public void MdeEncryptionTypesContractTest() 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()); - TestEncryptionKeyWrapProvider testEncryptionKeyWrapProvider = new TestEncryptionKeyWrapProvider - { - DataEncryptionKeyCacheTimeToLive = TimeSpan.Zero - }; + TestKeyEncryptionKeyResolver testKeyEncryptionKeyResolver = new TestKeyEncryptionKeyResolver(); - CosmosClient encryptionCosmosClient = clientWithNoCaching.WithEncryption(testEncryptionKeyWrapProvider); + CosmosClient encryptionCosmosClient = clientWithNoCaching.WithEncryption(testKeyEncryptionKeyResolver, TestKeyEncryptionKeyResolver.Id, TimeSpan.Zero); 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. @@ -1893,7 +1838,7 @@ public async Task VerifyKekRevokeHandling() TestDoc testDoc1 = await MdeEncryptionTests.MdeCreateItemAsync(encryptionContainer); - testEncryptionKeyWrapProvider.RevokeAccessSet = true; + testKeyEncryptionKeyResolver.RevokeAccessSet = true; // try creating it and it should fail as it has been revoked. try @@ -1921,16 +1866,15 @@ await MdeEncryptionTests.ValidateQueryResultsAsync( } // for unwrap to succeed - testEncryptionKeyWrapProvider.RevokeAccessSet = false; + testKeyEncryptionKeyResolver.RevokeAccessSet = false; // lets rewrap it. await database.RewrapClientEncryptionKeyAsync("keywithRevokedKek", MdeEncryptionTests.metadata2); - testEncryptionKeyWrapProvider.RevokeAccessSet = true; + testKeyEncryptionKeyResolver.RevokeAccessSet = true; // Should fail but will try to fetch the lastest from the Backend and updates the cache. await MdeEncryptionTests.MdeCreateItemAsync(encryptionContainer); - testEncryptionKeyWrapProvider.RevokeAccessSet = false; - testEncryptionKeyWrapProvider.DataEncryptionKeyCacheTimeToLive = TimeSpan.FromMinutes(120); + testKeyEncryptionKeyResolver.RevokeAccessSet = false; } [TestMethod] @@ -2210,7 +2154,7 @@ public async Task EncryptionTransactionalBatchWithCustomSerializer() .WithCustomSerializer(customSerializer) .Build()); - CosmosClient encryptionCosmosClientWithCustomSerializer = clientWithCustomSerializer.WithEncryption(new TestEncryptionKeyWrapProvider()); + CosmosClient encryptionCosmosClientWithCustomSerializer = clientWithCustomSerializer.WithEncryption(new TestKeyEncryptionKeyResolver(), TestKeyEncryptionKeyResolver.Id); Database databaseWithCustomSerializer = encryptionCosmosClientWithCustomSerializer.GetDatabase(MdeEncryptionTests.database.Id); Container encryptionContainerWithCustomSerializer = databaseWithCustomSerializer.GetContainer(MdeEncryptionTests.encryptionContainer.Id); @@ -2265,11 +2209,11 @@ await MdeEncryptionTests.ValidateQueryResultsAsync( } [TestMethod] - public async Task ValidateCachingofProtectedDataEncryptionKey() + public async Task ValidateCachingOfProtectedDataEncryptionKey() { - // Default cache TTL 2 hours. - TestEncryptionKeyWrapProvider newtestEncryptionKeyWrapProvider = new TestEncryptionKeyWrapProvider(); - CosmosClient newEncryptionClient = MdeEncryptionTests.client.WithEncryption(newtestEncryptionKeyWrapProvider); + // Default cache TTL 1 hours. + TestKeyEncryptionKeyResolver newtestKeyEncryptionKeyResolver = new TestKeyEncryptionKeyResolver(); + CosmosClient newEncryptionClient = MdeEncryptionTests.client.WithEncryption(newtestKeyEncryptionKeyResolver, TestKeyEncryptionKeyResolver.Id); Database database = newEncryptionClient.GetDatabase(MdeEncryptionTests.database.Id); Container encryptionContainer = database.GetContainer(MdeEncryptionTests.encryptionContainer.Id); @@ -2279,12 +2223,12 @@ public async Task ValidateCachingofProtectedDataEncryptionKey() await MdeEncryptionTests.MdeCreateItemAsync(encryptionContainer); } - newtestEncryptionKeyWrapProvider.UnWrapKeyCallsCount.TryGetValue(metadata1.Value, out int unwrapcount); + newtestKeyEncryptionKeyResolver.UnWrapKeyCallsCount.TryGetValue(metadata1.Value, out int unwrapcount); // expecting just one unwrap. Assert.AreEqual(1, unwrapcount); // no caching. - newtestEncryptionKeyWrapProvider.DataEncryptionKeyCacheTimeToLive = TimeSpan.Zero; + newEncryptionClient = MdeEncryptionTests.client.WithEncryption(newtestKeyEncryptionKeyResolver, TestKeyEncryptionKeyResolver.Id, TimeSpan.Zero); database = newEncryptionClient.GetDatabase(MdeEncryptionTests.database.Id); @@ -2295,7 +2239,7 @@ public async Task ValidateCachingofProtectedDataEncryptionKey() await MdeEncryptionTests.MdeCreateItemAsync(encryptionContainer); } - newtestEncryptionKeyWrapProvider.UnWrapKeyCallsCount.TryGetValue(metadata1.Value, out unwrapcount); + newtestKeyEncryptionKeyResolver.UnWrapKeyCallsCount.TryGetValue(metadata1.Value, out unwrapcount); Assert.IsTrue(unwrapcount > 1, "The actual unwrap count was not greater than 1"); } @@ -2447,8 +2391,8 @@ private async Task ValidateChangeFeedProcessorResponse( Assert.AreEqual(changeFeedReturnedDocs.Count, 2); - VerifyExpectedDocResponse(testDoc1, changeFeedReturnedDocs[changeFeedReturnedDocs.Count - 2]); - VerifyExpectedDocResponse(testDoc2, changeFeedReturnedDocs[changeFeedReturnedDocs.Count - 1]); + VerifyExpectedDocResponse(testDoc1, changeFeedReturnedDocs[^2]); + VerifyExpectedDocResponse(testDoc2, changeFeedReturnedDocs[^1]); if (leaseDatabase != null) { @@ -2495,8 +2439,8 @@ private async Task ValidateChangeFeedProcessorWithFeedHandlerResponse( Assert.AreEqual(changeFeedReturnedDocs.Count, 2); - VerifyExpectedDocResponse(testDoc1, changeFeedReturnedDocs[changeFeedReturnedDocs.Count - 2]); - VerifyExpectedDocResponse(testDoc2, changeFeedReturnedDocs[changeFeedReturnedDocs.Count - 1]); + VerifyExpectedDocResponse(testDoc1, changeFeedReturnedDocs[^2]); + VerifyExpectedDocResponse(testDoc2, changeFeedReturnedDocs[^1]); if (leaseDatabase != null) { @@ -2544,8 +2488,8 @@ private async Task ValidateChangeFeedProcessorWithManualCheckpointResponse( Assert.AreEqual(changeFeedReturnedDocs.Count, 2); - VerifyExpectedDocResponse(testDoc1, changeFeedReturnedDocs[changeFeedReturnedDocs.Count - 2]); - VerifyExpectedDocResponse(testDoc2, changeFeedReturnedDocs[changeFeedReturnedDocs.Count - 1]); + VerifyExpectedDocResponse(testDoc1, changeFeedReturnedDocs[^2]); + VerifyExpectedDocResponse(testDoc2, changeFeedReturnedDocs[^1]); if (leaseDatabase != null) { @@ -3303,64 +3247,97 @@ public Stream ToStream() } } - internal class TestEncryptionKeyWrapProvider : EncryptionKeyWrapProvider + internal class TestKeyEncryptionKey : IKeyEncryptionKey { - readonly Dictionary keyinfo = new Dictionary + private static readonly Dictionary keyinfo = new Dictionary { {"tempmetadata1", 1}, {"tempmetadata2", 2}, }; - public bool RevokeAccessSet { get; set; } + private readonly TestKeyEncryptionKeyResolver resolver; - public Dictionary WrapKeyCallsCount { get; set; } - - public Dictionary UnWrapKeyCallsCount { get; set; } - - public TestEncryptionKeyWrapProvider() + public TestKeyEncryptionKey(string keyId, TestKeyEncryptionKeyResolver resolver) { - this.WrapKeyCallsCount = new Dictionary(); - this.UnWrapKeyCallsCount = new Dictionary(); - this.RevokeAccessSet = false; + this.KeyId = keyId; + this.resolver = resolver; } - public override string ProviderName => "TESTKEYSTORE_VAULT"; + public string KeyId { get; } - public override Task UnwrapKeyAsync(string masterKeyPath, string keyEncryptionKeyAlgorithm, byte[] encryptedKey) + public byte[] UnwrapKey(string algorithm, ReadOnlyMemory encryptedKey, CancellationToken cancellationToken = default) { - if (masterKeyPath.Equals("revokedKek-metadata") && this.RevokeAccessSet) + if (this.KeyId.Equals("revokedKek-metadata") && this.resolver.RevokeAccessSet) { throw new RequestFailedException((int)HttpStatusCode.Forbidden, "Forbidden"); } - if (!this.UnWrapKeyCallsCount.ContainsKey(masterKeyPath)) + if (!this.resolver.UnWrapKeyCallsCount.ContainsKey(this.KeyId)) { - this.UnWrapKeyCallsCount[masterKeyPath] = 1; + this.resolver.UnWrapKeyCallsCount[this.KeyId] = 1; } else { - this.UnWrapKeyCallsCount[masterKeyPath]++; + this.resolver.UnWrapKeyCallsCount[this.KeyId]++; } - this.keyinfo.TryGetValue(masterKeyPath, out int moveBy); - byte[] plainkey = encryptedKey.Select(b => (byte)(b - moveBy)).ToArray(); - return Task.FromResult(plainkey); + keyinfo.TryGetValue(this.KeyId, out int moveBy); + byte[] plainkey = encryptedKey.ToArray().Select(b => (byte)(b - moveBy)).ToArray(); + return plainkey; + } + + public Task UnwrapKeyAsync(string algorithm, ReadOnlyMemory encryptedKey, CancellationToken cancellationToken = default) + { + return Task.FromResult(this.UnwrapKey(algorithm, encryptedKey, cancellationToken)); } - public override Task WrapKeyAsync(string masterKeyPath, string keyEncryptionKeyAlgorithm, byte[] key) + public byte[] WrapKey(string algorithm, ReadOnlyMemory key, CancellationToken cancellationToken = default) { - if (!this.WrapKeyCallsCount.ContainsKey(masterKeyPath)) + if (!this.resolver.WrapKeyCallsCount.ContainsKey(this.KeyId)) { - this.WrapKeyCallsCount[masterKeyPath] = 1; + this.resolver.WrapKeyCallsCount[this.KeyId] = 1; } else { - this.WrapKeyCallsCount[masterKeyPath]++; + this.resolver.WrapKeyCallsCount[this.KeyId]++; } - this.keyinfo.TryGetValue(masterKeyPath, out int moveBy); - byte[] encryptedkey = key.Select(b => (byte)(b + moveBy)).ToArray(); - return Task.FromResult(encryptedkey); + keyinfo.TryGetValue(this.KeyId, out int moveBy); + byte[] encryptedkey = key.ToArray().Select(b => (byte)(b + moveBy)).ToArray(); + return encryptedkey; + } + + public Task WrapKeyAsync(string algorithm, ReadOnlyMemory key, CancellationToken cancellationToken = default) + { + return Task.FromResult(this.WrapKey(algorithm, key, cancellationToken)); + } + } + + internal class TestKeyEncryptionKeyResolver : IKeyEncryptionKeyResolver + { + public static string Id => "TESTKEYSTORE_VAULT"; + + public bool RevokeAccessSet { get; set; } + + public Dictionary WrapKeyCallsCount { get; set; } + + public Dictionary UnWrapKeyCallsCount { get; set; } + + public TestKeyEncryptionKeyResolver() + { + this.WrapKeyCallsCount = new Dictionary(); + this.UnWrapKeyCallsCount = new Dictionary(); + this.RevokeAccessSet = false; + } + + public IKeyEncryptionKey Resolve(string keyId, CancellationToken cancellationToken = default) + { + return new TestKeyEncryptionKey(keyId, this); + } + + public Task ResolveAsync(string keyId, CancellationToken cancellationToken = default) + { + return Task.FromResult(this.Resolve(keyId, cancellationToken)); } } 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 8792a2d59c..777fd87529 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,37 +1,6 @@ { "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": { + "Microsoft.Azure.Cosmos.Encryption.EncryptionAlgorithm;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": { @@ -80,12 +49,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.Azure.Cosmos.Encryption.EncryptionKeyWrapProvider)[System.Runtime.CompilerServices.ExtensionAttribute()]": { + "Microsoft.Azure.Cosmos.CosmosClient WithEncryption(Microsoft.Azure.Cosmos.CosmosClient, Azure.Core.Cryptography.IKeyEncryptionKeyResolver, System.String, System.Nullable`1[System.TimeSpan])[System.Runtime.CompilerServices.ExtensionAttribute()]": { "Type": "Method", "Attributes": [ "ExtensionAttribute" ], - "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;" + "MethodInfo": "Microsoft.Azure.Cosmos.CosmosClient WithEncryption(Microsoft.Azure.Cosmos.CosmosClient, Azure.Core.Cryptography.IKeyEncryptionKeyResolver, System.String, System.Nullable`1[System.TimeSpan]);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" } }, "NestedTypes": {} @@ -112,79 +81,6 @@ }, "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": { @@ -201,13 +97,13 @@ }, "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": { + "Microsoft.Azure.Cosmos.Encryption.KeyEncryptionKeyResolverName;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": { + "System.String AzureKeyVault": { "Type": "Field", "Attributes": [], - "MethodInfo": "System.String RsaOaep;IsInitOnly:False;IsStatic:True;" + "MethodInfo": "System.String AzureKeyVault;IsInitOnly:False;IsStatic:True;" } }, "NestedTypes": {}