diff --git a/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs b/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs index 5eccc58c7a..071f704bc2 100644 --- a/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs +++ b/Microsoft.Azure.Cosmos.Encryption/tests/EmulatorTests/MdeEncryptionTests.cs @@ -45,8 +45,8 @@ public static async Task ClassInitialize(TestContext context) DataEncryptionKeyCacheTimeToLive = null }; - metadata1 = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, "key1", "tempmetadata1"); - metadata2 = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, "key2", "tempmetadata2"); + metadata1 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, "key1", "tempmetadata1"); + metadata2 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, "key2", "tempmetadata2"); MdeEncryptionTests.encryptionCosmosClient = MdeEncryptionTests.client.WithEncryption(testEncryptionKeyWrapProvider); MdeEncryptionTests.database = await MdeEncryptionTests.encryptionCosmosClient.CreateDatabaseAsync(Guid.NewGuid().ToString()); @@ -60,7 +60,7 @@ await MdeEncryptionTests.CreateClientEncryptionKeyAsync( metadata2); - EncryptionKeyWrapMetadata revokedKekmetadata = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, "revokedKek", "revokedKek-metadata"); + EncryptionKeyWrapMetadata revokedKekmetadata = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, "revokedKek", "revokedKek-metadata"); await database.CreateClientEncryptionKeyAsync( "keywithRevokedKek", DataEncryptionKeyAlgorithm.AeadAes256CbcHmacSha256, @@ -257,18 +257,18 @@ public async Task EncryptionCreateClientEncryptionKey() { string cekId = "anotherCek"; - EncryptionKeyWrapMetadata metadata1 = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata1"); + EncryptionKeyWrapMetadata metadata1 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata1"); ClientEncryptionKeyProperties clientEncryptionKeyProperties = await MdeEncryptionTests.CreateClientEncryptionKeyAsync( cekId, metadata1); Assert.AreEqual( - new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, name: cekId, value: metadata1.Value), + MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, name: cekId, value: metadata1.Value), clientEncryptionKeyProperties.EncryptionKeyWrapMetadata); // creating another key with same id should fail - metadata1 = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata2"); + metadata1 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata2"); try { @@ -289,22 +289,22 @@ await MdeEncryptionTests.CreateClientEncryptionKeyAsync( public async Task EncryptionRewrapClientEncryptionKey() { string cekId = "rewrapkeytest"; - EncryptionKeyWrapMetadata metadata1 = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata1"); + EncryptionKeyWrapMetadata metadata1 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata1"); ClientEncryptionKeyProperties clientEncryptionKeyProperties = await MdeEncryptionTests.CreateClientEncryptionKeyAsync( cekId, metadata1); Assert.AreEqual( - new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, name: cekId, value: metadata1.Value), + MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, name: cekId, value: metadata1.Value), clientEncryptionKeyProperties.EncryptionKeyWrapMetadata); - EncryptionKeyWrapMetadata updatedMetaData = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, metadata1 + "updatedmetadata"); + EncryptionKeyWrapMetadata updatedMetaData = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, metadata1 + "updatedmetadata"); clientEncryptionKeyProperties = await MdeEncryptionTests.RewarpClientEncryptionKeyAsync( cekId, updatedMetaData); Assert.AreEqual( - new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, name: cekId, value: updatedMetaData.Value), + MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, name: cekId, value: updatedMetaData.Value), clientEncryptionKeyProperties.EncryptionKeyWrapMetadata); } @@ -435,7 +435,7 @@ public async Task EncryptionResourceTokenAuthRestricted() try { string cekId = "testingcekID"; - EncryptionKeyWrapMetadata metadata1 = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata1"); + EncryptionKeyWrapMetadata metadata1 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata1"); ClientEncryptionKeyResponse clientEncrytionKeyResponse = await databaseForRestrictedUser.CreateClientEncryptionKeyAsync( cekId, @@ -450,7 +450,7 @@ public async Task EncryptionResourceTokenAuthRestricted() try { string cekId = "testingcekID"; - EncryptionKeyWrapMetadata metadata1 = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata1" + "updated"); + EncryptionKeyWrapMetadata metadata1 = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, cekId, "testmetadata1" + "updated"); ClientEncryptionKeyResponse clientEncrytionKeyResponse = await databaseForRestrictedUser.RewrapClientEncryptionKeyAsync( cekId, @@ -1549,7 +1549,7 @@ public async Task EncryptionValidatePolicyRefreshPostDatabaseDelete() DataEncryptionKeyCacheTimeToLive = TimeSpan.FromMinutes(30), }; - EncryptionKeyWrapMetadata keyWrapMetadata = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, "myCek", "mymetadata1"); + EncryptionKeyWrapMetadata keyWrapMetadata = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider.ProviderName, "myCek", "mymetadata1"); CosmosClient encryptionCosmosClient = mainClient.WithEncryption(testEncryptionKeyWrapProvider); Database mainDatabase = await encryptionCosmosClient.CreateDatabaseAsync("databaseToBeDeleted"); @@ -1615,7 +1615,7 @@ public async Task EncryptionValidatePolicyRefreshPostDatabaseDelete() mainDatabase = await encryptionCosmosClient.CreateDatabaseAsync("databaseToBeDeleted"); - keyWrapMetadata = new EncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider2.ProviderName, "myCek", "mymetadata2"); + keyWrapMetadata = MdeEncryptionTests.CreateEncryptionKeyWrapMetadata(testEncryptionKeyWrapProvider2.ProviderName, "myCek", "mymetadata2"); clientEncrytionKeyResponse = await mainDatabase.CreateClientEncryptionKeyAsync( keyWrapMetadata.Name, DataEncryptionKeyAlgorithm.AeadAes256CbcHmacSha256, @@ -2983,6 +2983,15 @@ private static void VerifyDiagnostics( } } + private static EncryptionKeyWrapMetadata CreateEncryptionKeyWrapMetadata(string type, string name, string value) + { +#if SDKPROJECTREF + return new EncryptionKeyWrapMetadata(type, name, value, "algo"); +#else + return new EncryptionKeyWrapMetadata(type, name, value); +#endif + } + public class TestDoc { diff --git a/Microsoft.Azure.Cosmos/src/Resource/ClientEncryptionKey/EncryptionKeyWrapMetadata.cs b/Microsoft.Azure.Cosmos/src/Resource/ClientEncryptionKey/EncryptionKeyWrapMetadata.cs index a5bc5e26aa..8047558303 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/ClientEncryptionKey/EncryptionKeyWrapMetadata.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/ClientEncryptionKey/EncryptionKeyWrapMetadata.cs @@ -10,7 +10,8 @@ namespace Microsoft.Azure.Cosmos using Newtonsoft.Json.Linq; /// - /// Metadata that a key wrapping provider can use to wrap/unwrap data encryption keys. + /// Metadata that can be used to wrap/unwrap a Data Encryption Key using a Customer Managed Key. + /// See https://aka.ms/CosmosClientEncryption for more information on client-side encryption support in Azure Cosmos DB. /// #if PREVIEW public @@ -27,14 +28,16 @@ private EncryptionKeyWrapMetadata() /// /// Creates a new instance of key wrap metadata. /// - /// ProviderName of KeyStoreProvider. - /// Name of the metadata. - /// Value of the metadata. - public EncryptionKeyWrapMetadata(string type, string name, string value) + /// Identifier for the key resolver. + /// Identifier for the customer managed key. + /// Path to the customer managed key. + /// Algorithm used in wrapping and unwrapping of the data encryption key. + public EncryptionKeyWrapMetadata(string type, string name, string value, string algorithm) { this.Type = type ?? throw new ArgumentNullException(nameof(type)); this.Name = name ?? throw new ArgumentNullException(nameof(name)); this.Value = value ?? throw new ArgumentNullException(nameof(value)); + this.Algorithm = algorithm ?? throw new ArgumentNullException(nameof(algorithm)); } /// @@ -42,7 +45,7 @@ public EncryptionKeyWrapMetadata(string type, string name, string value) /// /// Existing instance from which to initialize. public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source) - : this(source?.Type, source?.Name, source?.Value) + : this(source?.Type, source?.Name, source?.Value, source?.Algorithm) { } @@ -70,6 +73,14 @@ public EncryptionKeyWrapMetadata(EncryptionKeyWrapMetadata source) [JsonProperty(PropertyName = "value", NullValueHandling = NullValueHandling.Ignore)] public string Value { get; private set; } + /// + /// Serialized form of metadata. + /// Note: This value is saved in the Cosmos DB service. + /// Implementors of derived implementations should ensure that this does not have (private) key material or credential information. + /// + [JsonProperty(PropertyName = "algorithm", NullValueHandling = NullValueHandling.Ignore)] + public string Algorithm { get; private set; } + /// /// This contains additional values for scenarios where the SDK is not aware of new fields. /// This ensures that if resource is read and updated none of the fields will be lost in the process. @@ -91,6 +102,7 @@ public override int GetHashCode() hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.Type); hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.Name); hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.Value); + hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(this.Algorithm); return hashCode; } @@ -107,6 +119,7 @@ public bool Equals(EncryptionKeyWrapMetadata other) this.Type == other.Type && this.Name == other.Name && this.Value == other.Value && + this.Algorithm == other.Algorithm && this.AdditionalProperties.EqualsTo(other.AdditionalProperties); } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosDatabaseTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosDatabaseTests.cs index b61b94757b..9370345d81 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosDatabaseTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosDatabaseTests.cs @@ -416,7 +416,7 @@ public async Task EncryptionCreateReplaceCek() Assert.IsNotNull(cekProperties.ResourceId); Assert.AreEqual( - new EncryptionKeyWrapMetadata("custom", "metadataName", "metadataValue"), + new EncryptionKeyWrapMetadata("custom", "metadataName", "metadataValue", "algo"), cekProperties.EncryptionKeyWrapMetadata); // Use a different client instance to avoid (unintentional) cache impact @@ -433,7 +433,7 @@ public async Task EncryptionCreateReplaceCek() Assert.IsNotNull(cekProperties.ResourceId); Assert.AreEqual( - new EncryptionKeyWrapMetadata("custom", "metadataName", "updatedMetadataValue"), + new EncryptionKeyWrapMetadata("custom", "metadataName", "updatedMetadataValue", "algo"), cekProperties.EncryptionKeyWrapMetadata); // Use a different client instance to avoid (unintentional) cache impact @@ -450,7 +450,7 @@ private static async Task CreateCekAsync(Database // Generate random bytes cryptographically. byte[] rawCek = RandomNumberGenerator.GetBytes(32); - ClientEncryptionKeyProperties cekProperties = new ClientEncryptionKeyProperties(cekId, "AEAD_AES_256_CBC_HMAC_SHA256", rawCek, new EncryptionKeyWrapMetadata("custom", "metadataName", "metadataValue")); + ClientEncryptionKeyProperties cekProperties = new ClientEncryptionKeyProperties(cekId, "AEAD_AES_256_CBC_HMAC_SHA256", rawCek, new EncryptionKeyWrapMetadata("custom", "metadataName", "metadataValue", "algo")); ClientEncryptionKeyResponse cekResponse = await databaseCore.CreateClientEncryptionKeyAsync(cekProperties); @@ -474,7 +474,7 @@ private static async Task ReplaceCekAsync(Databas // Generate random bytes cryptographically. byte[] rawCek = RandomNumberGenerator.GetBytes(32); - ClientEncryptionKeyProperties cekProperties = new ClientEncryptionKeyProperties(cekId, "AEAD_AES_256_CBC_HMAC_SHA256", rawCek, new EncryptionKeyWrapMetadata("custom", "metadataName", "updatedMetadataValue")); + ClientEncryptionKeyProperties cekProperties = new ClientEncryptionKeyProperties(cekId, "AEAD_AES_256_CBC_HMAC_SHA256", rawCek, new EncryptionKeyWrapMetadata("custom", "metadataName", "updatedMetadataValue", "algo")); ClientEncryptionKeyResponse cekResponse = await cek.ReplaceAsync(cekProperties); Assert.AreEqual(HttpStatusCode.OK, cekResponse.StatusCode); @@ -501,7 +501,7 @@ public async Task VerifyCekFeedIterator() // Generate random bytes cryptographically. byte[] rawCek1 = RandomNumberGenerator.GetBytes(32); - ClientEncryptionKeyProperties cekProperties = new ClientEncryptionKeyProperties(cekId, "AEAD_AES_256_CBC_HMAC_SHA256", rawCek1, new EncryptionKeyWrapMetadata("custom", "metadataName", "metadataValue")); + ClientEncryptionKeyProperties cekProperties = new ClientEncryptionKeyProperties(cekId, "AEAD_AES_256_CBC_HMAC_SHA256", rawCek1, new EncryptionKeyWrapMetadata("custom", "metadataName", "metadataValue", "algo")); ClientEncryptionKeyResponse cekResponse = await databaseCore.CreateClientEncryptionKeyAsync(cekProperties); @@ -512,7 +512,7 @@ public async Task VerifyCekFeedIterator() // Generate random bytes cryptographically. byte[] rawCek2 = RandomNumberGenerator.GetBytes(32); - cekProperties = new ClientEncryptionKeyProperties(cekId, "AEAD_AES_256_CBC_HMAC_SHA256", rawCek2, new EncryptionKeyWrapMetadata("custom", "metadataName", "metadataValue")); + cekProperties = new ClientEncryptionKeyProperties(cekId, "AEAD_AES_256_CBC_HMAC_SHA256", rawCek2, new EncryptionKeyWrapMetadata("custom", "metadataName", "metadataValue", "algo")); cekResponse = await databaseCore.CreateClientEncryptionKeyAsync(cekProperties); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/TestCommon.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/TestCommon.cs index 342422ac0e..97a5771620 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/TestCommon.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/TestCommon.cs @@ -369,7 +369,7 @@ internal static async Task CreateClientEncryptionKey( string dekId, DatabaseInlineCore databaseInlineCore) { - EncryptionKeyWrapMetadata metadata = new EncryptionKeyWrapMetadata("custom", dekId, "tempMetadata"); + EncryptionKeyWrapMetadata metadata = new EncryptionKeyWrapMetadata("custom", dekId, "tempMetadata", "algo"); // Generate random bytes cryptographically. byte[] wrappedDataEncryptionKey = RandomNumberGenerator.GetBytes(32); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.json b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.json index ad7e48bafb..1d51c448db 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.json +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.json @@ -649,6 +649,20 @@ "Attributes": [], "MethodInfo": "Int32 GetHashCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, + "System.String Algorithm[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = 1, PropertyName = \"algorithm\")]": { + "Type": "Property", + "Attributes": [ + "JsonPropertyAttribute" + ], + "MethodInfo": "System.String Algorithm;CanRead:True;CanWrite:True;System.String get_Algorithm();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.String get_Algorithm()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.String get_Algorithm();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "System.String get_Name()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", "Attributes": [ @@ -696,10 +710,10 @@ "Attributes": [], "MethodInfo": "[Void .ctor(Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata), Void .ctor(Microsoft.Azure.Cosmos.EncryptionKeyWrapMetadata)]" }, - "Void .ctor(System.String, System.String, System.String)": { + "Void .ctor(System.String, System.String, System.String, System.String)": { "Type": "Constructor", "Attributes": [], - "MethodInfo": "[Void .ctor(System.String, System.String, System.String), Void .ctor(System.String, System.String, System.String)]" + "MethodInfo": "[Void .ctor(System.String, System.String, System.String, System.String), Void .ctor(System.String, System.String, System.String, System.String)]" } }, "NestedTypes": {}