Skip to content

Commit

Permalink
Merge branch 'master' into users/ealsur/gatewayrequestst
Browse files Browse the repository at this point in the history
  • Loading branch information
ealsur committed Jan 4, 2022
2 parents 5ae77bb + e3fe30a commit ec8f4ce
Show file tree
Hide file tree
Showing 16 changed files with 627 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public static class EncryptionContainerExtensions
/// <code language="c#">
/// <![CDATA[
/// CosmosClient cosmosClient = new CosmosClient();
/// cosmosClient.WithEncryption(azureKeyVaultKeyStoreProvider);
/// cosmosClient.WithEncryption(azureKeyVaultKeyWrapProvider);
/// containerWithEncryption = await this.cosmosDatabase.GetContainer("id").InitializeEncryptionAsync();
/// ]]>
/// </code>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,26 @@ namespace Microsoft.Azure.Cosmos.Encryption
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Data.Encryption.Cryptography;

/// <summary>
/// CosmosClient with Encryption support.
/// </summary>
internal sealed class EncryptionCosmosClient : CosmosClient
{
internal static readonly SemaphoreSlim EncryptionKeyCacheSemaphore = new SemaphoreSlim(1, 1);

private readonly CosmosClient cosmosClient;

private readonly AsyncCache<string, ClientEncryptionKeyProperties> clientEncryptionKeyPropertiesCacheByKeyId;

public EncryptionCosmosClient(CosmosClient cosmosClient, EncryptionKeyStoreProvider encryptionKeyStoreProvider)
public EncryptionCosmosClient(CosmosClient cosmosClient, EncryptionKeyWrapProvider encryptionKeyWrapProvider)
{
this.cosmosClient = cosmosClient ?? throw new ArgumentNullException(nameof(cosmosClient));
this.EncryptionKeyStoreProvider = encryptionKeyStoreProvider ?? throw new ArgumentNullException(nameof(encryptionKeyStoreProvider));
this.EncryptionKeyWrapProvider = encryptionKeyWrapProvider ?? throw new ArgumentNullException(nameof(encryptionKeyWrapProvider));
this.clientEncryptionKeyPropertiesCacheByKeyId = new AsyncCache<string, ClientEncryptionKeyProperties>();
}

public EncryptionKeyStoreProvider EncryptionKeyStoreProvider { get; }
public EncryptionKeyWrapProvider EncryptionKeyWrapProvider { get; }

public override CosmosClientOptions ClientOptions => this.cosmosClient.ClientOptions;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,23 @@ public static class EncryptionCosmosClientExtensions
/// Get Cosmos Client with Encryption support for performing operations using client-side encryption.
/// </summary>
/// <param name="cosmosClient">Regular Cosmos Client.</param>
/// <param name="encryptionKeyStoreProvider">EncryptionKeyStoreProvider, provider that allows interaction with the master keys.</param>
/// <param name="encryptionKeyWrapProvider">EncryptionKeyWrapProvider, provider that allows interaction with the master keys.</param>
/// <returns> CosmosClient to perform operations supporting client-side encryption / decryption.</returns>
public static CosmosClient WithEncryption(
this CosmosClient cosmosClient,
EncryptionKeyStoreProvider encryptionKeyStoreProvider)
EncryptionKeyWrapProvider encryptionKeyWrapProvider)
{
if (encryptionKeyStoreProvider == null)
if (encryptionKeyWrapProvider == null)
{
throw new ArgumentNullException(nameof(encryptionKeyStoreProvider));
throw new ArgumentNullException(nameof(encryptionKeyWrapProvider));
}

if (cosmosClient == null)
{
throw new ArgumentNullException(nameof(cosmosClient));
}

// set the TTL for ProtectedDataEncryption at the Encryption CosmosClient Init so that we have a uniform expiry of the KeyStoreProvider and ProtectedDataEncryption cache items.
if (encryptionKeyStoreProvider.DataEncryptionKeyCacheTimeToLive.HasValue)
{
ProtectedDataEncryptionKey.TimeToLive = encryptionKeyStoreProvider.DataEncryptionKeyCacheTimeToLive.Value;
}
else
{
// If null is passed to DataEncryptionKeyCacheTimeToLive it results in forever caching hence setting
// arbitrarily large caching period. ProtectedDataEncryptionKey does not seem to handle TimeSpan.MaxValue.
ProtectedDataEncryptionKey.TimeToLive = TimeSpan.FromDays(36500);
}

return new EncryptionCosmosClient(cosmosClient, encryptionKeyStoreProvider);
return new EncryptionCosmosClient(cosmosClient, encryptionKeyWrapProvider);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public static class EncryptionDatabaseExtensions
public static async Task<ClientEncryptionKeyResponse> CreateClientEncryptionKeyAsync(
this Database database,
string clientEncryptionKeyId,
DataEncryptionKeyAlgorithm dataEncryptionKeyAlgorithm,
string dataEncryptionKeyAlgorithm,
EncryptionKeyWrapMetadata encryptionKeyWrapMetadata,
CancellationToken cancellationToken = default)
{
Expand All @@ -49,11 +49,9 @@ public static async Task<ClientEncryptionKeyResponse> CreateClientEncryptionKeyA
throw new ArgumentNullException(nameof(clientEncryptionKeyId));
}

string encryptionAlgorithm = dataEncryptionKeyAlgorithm.ToString();

if (!string.Equals(encryptionAlgorithm, DataEncryptionKeyAlgorithm.AEAD_AES_256_CBC_HMAC_SHA256.ToString()))
if (!string.Equals(dataEncryptionKeyAlgorithm, DataEncryptionKeyAlgorithm.AeadAes256CbcHmacSha256))
{
throw new ArgumentException($"Invalid Encryption Algorithm '{encryptionAlgorithm}' passed. Please refer to https://aka.ms/CosmosClientEncryption for more details. ");
throw new ArgumentException($"Invalid Encryption Algorithm '{dataEncryptionKeyAlgorithm}' passed. Please refer to https://aka.ms/CosmosClientEncryption for more details. ");
}

if (encryptionKeyWrapMetadata == null)
Expand All @@ -65,17 +63,17 @@ public static async Task<ClientEncryptionKeyResponse> CreateClientEncryptionKeyA
? encryptionDatabase.EncryptionCosmosClient
: throw new ArgumentException("Creating a ClientEncryptionKey resource requires the use of an encryption - enabled client. Please refer to https://aka.ms/CosmosClientEncryption for more details. ");

EncryptionKeyStoreProvider encryptionKeyStoreProvider = encryptionCosmosClient.EncryptionKeyStoreProvider;
EncryptionKeyWrapProvider encryptionKeyWrapProvider = encryptionCosmosClient.EncryptionKeyWrapProvider;

if (!string.Equals(encryptionKeyWrapMetadata.Type, encryptionKeyStoreProvider.ProviderName))
if (!string.Equals(encryptionKeyWrapMetadata.Type, encryptionKeyWrapProvider.ProviderName))
{
throw new ArgumentException("The EncryptionKeyWrapMetadata Type value does not match with the ProviderName of EncryptionKeyStoreProvider configured on the Client. Please refer to https://aka.ms/CosmosClientEncryption for more details. ");
throw new ArgumentException("The EncryptionKeyWrapMetadata Type value does not match with the ProviderName of EncryptionKeyWrapProvider configured on the Client. Please refer to https://aka.ms/CosmosClientEncryption for more details. ");
}

KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate(
encryptionKeyWrapMetadata.Name,
encryptionKeyWrapMetadata.Value,
encryptionKeyStoreProvider);
encryptionKeyWrapProvider.EncryptionKeyStoreProviderImpl);

ProtectedDataEncryptionKey protectedDataEncryptionKey = new ProtectedDataEncryptionKey(
clientEncryptionKeyId,
Expand All @@ -91,7 +89,7 @@ public static async Task<ClientEncryptionKeyResponse> CreateClientEncryptionKeyA

ClientEncryptionKeyProperties clientEncryptionKeyProperties = new ClientEncryptionKeyProperties(
clientEncryptionKeyId,
encryptionAlgorithm,
dataEncryptionKeyAlgorithm,
wrappedDataEncryptionKey,
encryptionKeyWrapMetadata);

Expand Down Expand Up @@ -145,11 +143,11 @@ public static async Task<ClientEncryptionKeyResponse> RewrapClientEncryptionKeyA
? encryptionDatabase.EncryptionCosmosClient
: throw new ArgumentException("Rewraping a ClientEncryptionKey requires the use of an encryption - enabled client. Please refer to https://aka.ms/CosmosClientEncryption for more details. ");

EncryptionKeyStoreProvider encryptionKeyStoreProvider = encryptionCosmosClient.EncryptionKeyStoreProvider;
EncryptionKeyWrapProvider encryptionKeyWrapProvider = encryptionCosmosClient.EncryptionKeyWrapProvider;

if (!string.Equals(newEncryptionKeyWrapMetadata.Type, encryptionKeyStoreProvider.ProviderName))
if (!string.Equals(newEncryptionKeyWrapMetadata.Type, encryptionKeyWrapProvider.ProviderName))
{
throw new ArgumentException("The EncryptionKeyWrapMetadata Type value does not match with the ProviderName of EncryptionKeyStoreProvider configured on the Client. Please refer to https://aka.ms/CosmosClientEncryption for more details. ");
throw new ArgumentException("The EncryptionKeyWrapMetadata Type value does not match with the ProviderName of EncryptionKeyWrapProvider configured on the Client. Please refer to https://aka.ms/CosmosClientEncryption for more details. ");
}

ClientEncryptionKeyProperties clientEncryptionKeyProperties = await clientEncryptionKey.ReadAsync(cancellationToken: cancellationToken);
Expand All @@ -162,14 +160,14 @@ public static async Task<ClientEncryptionKeyResponse> RewrapClientEncryptionKeyA
KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate(
clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Name,
clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Value,
encryptionKeyStoreProvider);
encryptionKeyWrapProvider.EncryptionKeyStoreProviderImpl);

byte[] unwrappedKey = keyEncryptionKey.DecryptEncryptionKey(clientEncryptionKeyProperties.WrappedDataEncryptionKey);

keyEncryptionKey = KeyEncryptionKey.GetOrCreate(
newEncryptionKeyWrapMetadata.Name,
newEncryptionKeyWrapMetadata.Value,
encryptionKeyStoreProvider);
encryptionKeyWrapProvider.EncryptionKeyStoreProviderImpl);

byte[] rewrappedKey = keyEncryptionKey.EncryptEncryptionKey(unwrappedKey);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,13 @@ namespace Microsoft.Azure.Cosmos.Encryption

internal sealed class EncryptionSettingForProperty
{
private static readonly SemaphoreSlim EncryptionKeyCacheSemaphore = new SemaphoreSlim(1, 1);

private readonly string databaseRid;

private readonly EncryptionContainer encryptionContainer;

public EncryptionSettingForProperty(
string clientEncryptionKeyId,
EncryptionType encryptionType,
Data.Encryption.Cryptography.EncryptionType encryptionType,
EncryptionContainer encryptionContainer,
string databaseRid)
{
Expand All @@ -33,7 +31,7 @@ public EncryptionSettingForProperty(

public string ClientEncryptionKeyId { get; }

public EncryptionType EncryptionType { get; }
public Data.Encryption.Cryptography.EncryptionType EncryptionType { get; }

public async Task<AeadAes256CbcHmac256EncryptionAlgorithm> BuildEncryptionAlgorithmForSettingAsync(CancellationToken cancellationToken)
{
Expand All @@ -52,7 +50,7 @@ public async Task<AeadAes256CbcHmac256EncryptionAlgorithm> BuildEncryptionAlgori
// Here a request is sent out to unwrap using the Master Key configured via the Key Encryption Key.
protectedDataEncryptionKey = await this.BuildProtectedDataEncryptionKeyAsync(
clientEncryptionKeyProperties,
this.encryptionContainer.EncryptionCosmosClient.EncryptionKeyStoreProvider,
this.encryptionContainer.EncryptionCosmosClient.EncryptionKeyWrapProvider,
this.ClientEncryptionKeyId,
cancellationToken);
}
Expand All @@ -75,7 +73,7 @@ public async Task<AeadAes256CbcHmac256EncryptionAlgorithm> BuildEncryptionAlgori
// try to build the ProtectedDataEncryptionKey. If it fails, try to force refresh the gateway cache and get the latest client encryption key.
protectedDataEncryptionKey = await this.BuildProtectedDataEncryptionKeyAsync(
clientEncryptionKeyProperties,
this.encryptionContainer.EncryptionCosmosClient.EncryptionKeyStoreProvider,
this.encryptionContainer.EncryptionCosmosClient.EncryptionKeyWrapProvider,
this.ClientEncryptionKeyId,
cancellationToken);
}
Expand Down Expand Up @@ -143,7 +141,7 @@ private async Task<ProtectedDataEncryptionKey> ForceRefreshGatewayCacheAndBuildP

ProtectedDataEncryptionKey protectedDataEncryptionKey = await this.BuildProtectedDataEncryptionKeyAsync(
clientEncryptionKeyProperties,
this.encryptionContainer.EncryptionCosmosClient.EncryptionKeyStoreProvider,
this.encryptionContainer.EncryptionCosmosClient.EncryptionKeyWrapProvider,
this.ClientEncryptionKeyId,
cancellationToken);

Expand All @@ -152,18 +150,18 @@ private async Task<ProtectedDataEncryptionKey> ForceRefreshGatewayCacheAndBuildP

private async Task<ProtectedDataEncryptionKey> BuildProtectedDataEncryptionKeyAsync(
ClientEncryptionKeyProperties clientEncryptionKeyProperties,
EncryptionKeyStoreProvider encryptionKeyStoreProvider,
EncryptionKeyWrapProvider encryptionKeyWrapProvider,
string keyId,
CancellationToken cancellationToken)
{
if (await EncryptionKeyCacheSemaphore.WaitAsync(-1, cancellationToken))
if (await EncryptionCosmosClient.EncryptionKeyCacheSemaphore.WaitAsync(-1, cancellationToken))
{
try
{
KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate(
clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Name,
clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Value,
encryptionKeyStoreProvider);
encryptionKeyWrapProvider.EncryptionKeyStoreProviderImpl);

ProtectedDataEncryptionKey protectedDataEncryptionKey = ProtectedDataEncryptionKey.GetOrCreate(
keyId,
Expand All @@ -174,7 +172,7 @@ private async Task<ProtectedDataEncryptionKey> BuildProtectedDataEncryptionKeyAs
}
finally
{
EncryptionKeyCacheSemaphore.Release(1);
EncryptionCosmosClient.EncryptionKeyCacheSemaphore.Release(1);
}
}

Expand Down
8 changes: 4 additions & 4 deletions Microsoft.Azure.Cosmos.Encryption/src/EncryptionSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ public void SetRequestHeaders(RequestOptions requestOptions)
};
}

private static EncryptionType GetEncryptionTypeForProperty(ClientEncryptionIncludedPath clientEncryptionIncludedPath)
private static Data.Encryption.Cryptography.EncryptionType GetEncryptionTypeForProperty(ClientEncryptionIncludedPath clientEncryptionIncludedPath)
{
return clientEncryptionIncludedPath.EncryptionType switch
{
CosmosEncryptionType.Deterministic => EncryptionType.Deterministic,
CosmosEncryptionType.Randomized => EncryptionType.Randomized,
EncryptionType.Deterministic => Data.Encryption.Cryptography.EncryptionType.Deterministic,
EncryptionType.Randomized => Data.Encryption.Cryptography.EncryptionType.Randomized,
_ => throw new ArgumentException($"Invalid encryption type {clientEncryptionIncludedPath.EncryptionType}. Please refer to https://aka.ms/CosmosClientEncryption for more details. "),
};
}
Expand Down Expand Up @@ -102,7 +102,7 @@ await encryptionContainer.EncryptionCosmosClient.GetClientEncryptionKeyPropertie
// update the property level setting.
foreach (ClientEncryptionIncludedPath propertyToEncrypt in clientEncryptionPolicy.IncludedPaths)
{
EncryptionType encryptionType = GetEncryptionTypeForProperty(propertyToEncrypt);
Data.Encryption.Cryptography.EncryptionType encryptionType = GetEncryptionTypeForProperty(propertyToEncrypt);

EncryptionSettingForProperty encryptionSettingsForProperty = new EncryptionSettingForProperty(
propertyToEncrypt.ClientEncryptionKeyId,
Expand Down
Loading

0 comments on commit ec8f4ce

Please sign in to comment.