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": {}