Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client Encryption: Adds an abstraction layer to hide the underlying usage of Microsoft Data Encryption(MDE) library. #2906

Merged
merged 30 commits into from
Jan 4, 2022
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
af6ec30
Abstract public facing MDE lib classes.
kr-santosh Nov 24, 2021
eb0ef0b
removed sign,verify and GetOrCreateSignatureVerificationResult from C…
kr-santosh Nov 24, 2021
b933661
Refactoring
kr-santosh Nov 24, 2021
2e2c02d
Update CosmosAzureKeyVaultKeyStoreProvider.cs
kr-santosh Nov 24, 2021
8f0ef92
reverts,fixes.
kr-santosh Nov 24, 2021
b6699cf
Refactoring
kr-santosh Nov 24, 2021
107e879
refactoring, fixed the names.
kr-santosh Nov 24, 2021
ca8009c
documentation fixes.
kr-santosh Nov 24, 2021
d17b540
Merge branch 'master' into users/sakulk/hideMdeLib
kr-santosh Nov 24, 2021
490912b
Update DotNetSDKEncryptionAPI.json
kr-santosh Nov 24, 2021
120ee51
Update CosmosEncryptionKeyStoreProviderCore.cs
kr-santosh Nov 25, 2021
7bc9d31
Update MdeEncryptionTests.cs
kr-santosh Nov 30, 2021
7b15029
Merge branch 'master' into users/sakulk/hideMdeLib
kr-santosh Dec 2, 2021
74ffbaa
Update MdeEncryptionTests.cs
kr-santosh Dec 2, 2021
431e6cf
Update MdeEncryptionTests.cs
kr-santosh Dec 2, 2021
e6d0225
Merge branch 'master' into users/sakulk/hideMdeLib
kr-santosh Dec 2, 2021
d222db9
Renamed CosmosEncryptionKeyStoreProviderCore and removed trustedendpo…
kr-santosh Dec 2, 2021
ea8e504
Merge branch 'master' into users/sakulk/hideMdeLib
kr-santosh Dec 6, 2021
52a819f
remove async from interfaces.
kr-santosh Dec 7, 2021
e9825f8
Merge branch 'master' into users/sakulk/hideMdeLib
kr-santosh Dec 7, 2021
9dc2f66
Update EncryptionSettingForProperty.cs
kr-santosh Dec 8, 2021
3ba1958
Changed the DataEncryptionKeyCacheTimeToLive contract which was virtu…
kr-santosh Dec 8, 2021
68fc65c
Fixes. Removed usage of additional akv cache.
kr-santosh Dec 9, 2021
ea0506e
Updated comments, call hierarchy
kr-santosh Dec 9, 2021
d8bae16
Removes Cosmos Prefix from constants, updates PDEK TTL update logic, …
kr-santosh Dec 15, 2021
5ab2719
Merge branch 'master' into users/sakulk/hideMdeLib
kr-santosh Dec 15, 2021
38fddd0
Update MdeEncryptionTests.cs
kr-santosh Dec 15, 2021
d30c64e
Update DotNetSDKEncryptionAPI.json
kr-santosh Dec 15, 2021
8f7e2ab
Moved the cache lock to EncryptionCosmosClient.
kr-santosh Dec 15, 2021
5984f7a
Merge branch 'master' into users/sakulk/hideMdeLib
j82w Jan 4, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ 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.
Expand All @@ -19,14 +18,14 @@ internal sealed class EncryptionCosmosClient : CosmosClient

private readonly AsyncCache<string, ClientEncryptionKeyProperties> clientEncryptionKeyPropertiesCacheByKeyId;

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

public EncryptionKeyStoreProvider EncryptionKeyStoreProvider { get; }
public CosmosEncryptionKeyStoreProvider CosmosEncryptionKeyStoreProvider { get; }

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ 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="cosmosEncryptionKeyStoreProvider">CosmosEncryptionKeyStoreProvider, 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)
CosmosEncryptionKeyStoreProvider cosmosEncryptionKeyStoreProvider)
{
if (encryptionKeyStoreProvider == null)
if (cosmosEncryptionKeyStoreProvider == null)
{
throw new ArgumentNullException(nameof(encryptionKeyStoreProvider));
throw new ArgumentNullException(nameof(cosmosEncryptionKeyStoreProvider));
}

if (cosmosClient == null)
Expand All @@ -33,9 +33,9 @@ public static CosmosClient WithEncryption(
}

// 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)
if (cosmosEncryptionKeyStoreProvider.DataEncryptionKeyCacheTimeToLive.HasValue)
{
ProtectedDataEncryptionKey.TimeToLive = encryptionKeyStoreProvider.DataEncryptionKeyCacheTimeToLive.Value;
ProtectedDataEncryptionKey.TimeToLive = cosmosEncryptionKeyStoreProvider.DataEncryptionKeyCacheTimeToLive.Value;
}
else
{
Expand All @@ -44,7 +44,7 @@ public static CosmosClient WithEncryption(
ProtectedDataEncryptionKey.TimeToLive = TimeSpan.FromDays(36500);
}

return new EncryptionCosmosClient(cosmosClient, encryptionKeyStoreProvider);
return new EncryptionCosmosClient(cosmosClient, cosmosEncryptionKeyStoreProvider);
}
}
}
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, CosmosDataEncryptionKeyAlgorithm.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;
CosmosEncryptionKeyStoreProvider cosmosEncryptionKeyStoreProvider = encryptionCosmosClient.CosmosEncryptionKeyStoreProvider;

if (!string.Equals(encryptionKeyWrapMetadata.Type, encryptionKeyStoreProvider.ProviderName))
if (!string.Equals(encryptionKeyWrapMetadata.Type, cosmosEncryptionKeyStoreProvider.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. ");
}

KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate(
encryptionKeyWrapMetadata.Name,
encryptionKeyWrapMetadata.Value,
encryptionKeyStoreProvider);
cosmosEncryptionKeyStoreProvider.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,9 +143,9 @@ 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;
CosmosEncryptionKeyStoreProvider cosmosEncryptionKeyStoreProvider = encryptionCosmosClient.CosmosEncryptionKeyStoreProvider;

if (!string.Equals(newEncryptionKeyWrapMetadata.Type, encryptionKeyStoreProvider.ProviderName))
if (!string.Equals(newEncryptionKeyWrapMetadata.Type, cosmosEncryptionKeyStoreProvider.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. ");
}
Expand All @@ -162,14 +160,14 @@ public static async Task<ClientEncryptionKeyResponse> RewrapClientEncryptionKeyA
KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate(
clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Name,
clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Value,
encryptionKeyStoreProvider);
cosmosEncryptionKeyStoreProvider.EncryptionKeyStoreProviderImpl);

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

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,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.CosmosEncryptionKeyStoreProvider,
this.ClientEncryptionKeyId,
cancellationToken);
}
Expand All @@ -75,7 +75,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.CosmosEncryptionKeyStoreProvider,
this.ClientEncryptionKeyId,
cancellationToken);
}
Expand Down Expand Up @@ -146,7 +146,7 @@ private async Task<ProtectedDataEncryptionKey> ForceRefreshGatewayCacheAndBuildP

private async Task<ProtectedDataEncryptionKey> BuildProtectedDataEncryptionKeyAsync(
ClientEncryptionKeyProperties clientEncryptionKeyProperties,
EncryptionKeyStoreProvider encryptionKeyStoreProvider,
CosmosEncryptionKeyStoreProvider cosmosEncryptionKeyStoreProvider,
string keyId,
CancellationToken cancellationToken)
{
Expand All @@ -157,7 +157,7 @@ private async Task<ProtectedDataEncryptionKey> BuildProtectedDataEncryptionKeyAs
KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate(
clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Name,
clientEncryptionKeyProperties.EncryptionKeyWrapMetadata.Value,
encryptionKeyStoreProvider);
cosmosEncryptionKeyStoreProvider.EncryptionKeyStoreProviderImpl);

ProtectedDataEncryptionKey protectedDataEncryptionKey = ProtectedDataEncryptionKey.GetOrCreate(
keyId,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//------------------------------------------------------------
// 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;
using Microsoft.Data.Encryption.Cryptography;

/// <summary>
/// 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.
/// </summary>
public sealed class CosmosAzureKeyVaultKeyStoreProvider : CosmosEncryptionKeyStoreProvider
{
private readonly AzureKeyVaultKeyStoreProvider azureKeyVaultKeyStoreProvider;
kr-santosh marked this conversation as resolved.
Show resolved Hide resolved

private TimeSpan? dataEncryptionKeyCacheTimeToLive;

/// <summary>
/// Initializes a new instance of the <see cref="CosmosAzureKeyVaultKeyStoreProvider"/> class.
/// Constructor that takes an implementation of Token Credential that is capable of providing an OAuth Token.
/// </summary>
/// <param name="tokenCredential"> returns token credentials. </param>
public CosmosAzureKeyVaultKeyStoreProvider(TokenCredential tokenCredential)
{
this.azureKeyVaultKeyStoreProvider = new AzureKeyVaultKeyStoreProvider(tokenCredential);
}

/// <inheritdoc/>
public override TimeSpan? DataEncryptionKeyCacheTimeToLive
{
get => this.dataEncryptionKeyCacheTimeToLive;
set => this.azureKeyVaultKeyStoreProvider.DataEncryptionKeyCacheTimeToLive = this.dataEncryptionKeyCacheTimeToLive = value;
}

/// <summary>
/// Gets name of the Encryption Key Store Provider implementation.
/// </summary>
public override string ProviderName => this.azureKeyVaultKeyStoreProvider.ProviderName;

/// <summary>
/// This function uses the asymmetric key specified by the key path
/// and decrypts an encrypted data dencryption key with RSA encryption algorithm.
/// </summary>.
/// <param name="encryptionKeyId">Identifier of an asymmetric key in Azure Key Vault. </param>
/// <param name="cosmosKeyEncryptionKeyAlgorithm">The key encryption algorithm.</param>
/// <param name="encryptedKey">The ciphertext key.</param>
/// <returns>Plain text data encryption key. </returns>
public override Task<byte[]> UnwrapKeyAsync(string encryptionKeyId, string cosmosKeyEncryptionKeyAlgorithm, byte[] encryptedKey)
{
KeyEncryptionKeyAlgorithm keyEncryptionKeyAlgorithm = cosmosKeyEncryptionKeyAlgorithm switch
{
CosmosKeyEncryptionKeyAlgorithm.RsaOaep => 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));
}

/// <summary>
/// This function uses the asymmetric key specified by the key path
/// and encrypts an unencrypted data encryption key with RSA encryption algorithm.
/// </summary>
/// <param name="encryptionKeyId">Identifier of an asymmetric key in Azure Key Vault. </param>
/// <param name="cosmosKeyEncryptionKeyAlgorithm">The key encryption algorithm.</param>
/// <param name="key">The plaintext key.</param>
/// <returns>Encrypted data encryption key. </returns>
public override Task<byte[]> WrapKeyAsync(string encryptionKeyId, string cosmosKeyEncryptionKeyAlgorithm, byte[] key)
{
KeyEncryptionKeyAlgorithm keyEncryptionKeyAlgorithm = cosmosKeyEncryptionKeyAlgorithm switch
{
CosmosKeyEncryptionKeyAlgorithm.RsaOaep => 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));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

namespace Microsoft.Azure.Cosmos.Encryption
{
/// <summary>
/// Represents the encryption algorithms supported for data encryption.
/// </summary>
public static class CosmosDataEncryptionKeyAlgorithm
kr-santosh marked this conversation as resolved.
Show resolved Hide resolved
kr-santosh marked this conversation as resolved.
Show resolved Hide resolved
{
/// <summary>
/// Represents the authenticated encryption algorithm with associated data as described in
/// http://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05.
/// </summary>
public const string AeadAes256CbcHmacSha256 = "AEAD_AES_256_CBC_HMAC_SHA256";
}
}
Loading