public async Task EncryptionUTCreateItemWithUnknownDek() { Container container = this.GetContainerWithMockSetup(); DatabaseCore database = (DatabaseCore)((ContainerCore)(ContainerInlineCore)container).Database; MyItem item = EncryptionUnitTests.GetNewItem(); try { await container.CreateItemAsync( item, new PartitionKey(item.PK), new ItemRequestOptions { EncryptionOptions = new EncryptionOptions { DataEncryptionKey = database.GetDataEncryptionKey("random"), PathsToEncrypt = MyItem.PathsToEncrypt } }); Assert.Fail(); } catch (CosmosException ex) { Assert.IsTrue(ex.Message.Contains(ClientResources.DataEncryptionKeyNotFound)); } }
internal override async Task <Stream> EncryptItemAsync( Stream input, EncryptionOptions encryptionOptions, DatabaseCore database, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { if (input == null) { throw new ArgumentException(ClientResources.InvalidRequestWithEncryptionOptions); } Debug.Assert(encryptionOptions != null); Debug.Assert(database != null); Debug.Assert(diagnosticsContext != null); using (diagnosticsContext.CreateScope("Encrypt")) { return(await this.EncryptionProcessor.EncryptAsync( input, encryptionOptions, database, this.ClientOptions.EncryptionKeyWrapProvider, diagnosticsContext, cancellationToken)); } }
private async Task <JObject> DecryptContentAsync( EncryptionProperties encryptionProperties, DatabaseCore database, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { if (encryptionProperties.EncryptionFormatVersion != 1) { throw CosmosExceptionFactory.CreateInternalServerErrorException($"Unknown encryption format version: {encryptionProperties.EncryptionFormatVersion}. Please upgrade your SDK to the latest version."); } DataEncryptionKeyCore tempDek = (DataEncryptionKeyInlineCore)database.GetDataEncryptionKey(id: "unknown"); (DataEncryptionKeyProperties _, InMemoryRawDek inMemoryRawDek) = await tempDek.FetchUnwrappedByRidAsync( encryptionProperties.DataEncryptionKeyRid, diagnosticsContext, cancellationToken); byte[] plainText = inMemoryRawDek.AlgorithmUsingRawDek.DecryptData(encryptionProperties.EncryptedData); JObject plainTextJObj = null; using (MemoryStream memoryStream = new MemoryStream(plainText)) using (StreamReader streamReader = new StreamReader(memoryStream)) using (JsonTextReader jsonTextReader = new JsonTextReader(streamReader)) { plainTextJObj = JObject.Load(jsonTextReader); } return(plainTextJObj); }
internal static Uri CreateLinkUri(CosmosClientContext clientContext, DatabaseCore database, string keyId) { return(clientContext.CreateLink( parentLink: database.LinkUri.OriginalString, uriPathSegment: Paths.ClientEncryptionKeysPathSegment, id: keyId)); }
public async Task EncryptionUTRewrapDekWithoutEncryptionSerializer() { string dekId = "mydek"; EncryptionTestHandler testHandler = new EncryptionTestHandler(); // Create a DEK using a properly setup client first Container container = this.GetContainerWithMockSetup(testHandler); DatabaseCore databaseWithSerializer = (DatabaseCore)((ContainerCore)(ContainerInlineCore)container).Database; DataEncryptionKeyResponse dekResponse = await databaseWithSerializer.CreateDataEncryptionKeyAsync(dekId, EncryptionUnitTests.Algo, this.metadata1); Assert.AreEqual(HttpStatusCode.Created, dekResponse.StatusCode); // Clear the handler pipeline that would have got setup testHandler.InnerHandler = null; // Ensure rewrap for this key fails on improperly configured client try { DatabaseCore database = (DatabaseCore)((ContainerCore)(ContainerInlineCore)this.GetContainer(testHandler)).Database; DataEncryptionKey dek = database.GetDataEncryptionKey(dekId); await dek.RewrapAsync(this.metadata2); Assert.Fail(); } catch (ArgumentException ex) { Assert.AreEqual(ClientResources.EncryptionKeyWrapProviderNotConfigured, ex.Message); } }
private Container GetContainer(EncryptionTestHandler encryptionTestHandler = null) { this.testHandler = encryptionTestHandler ?? new EncryptionTestHandler(); CosmosClient client = MockCosmosUtil.CreateMockCosmosClient((builder) => builder.AddCustomHandlers(this.testHandler)); DatabaseCore database = new DatabaseCore(client.ClientContext, EncryptionUnitTests.DatabaseId); return(new ContainerInlineCore(new ContainerCore(client.ClientContext, database, EncryptionUnitTests.ContainerId))); }
public async Task <Stream> DecryptAsync( Stream input, DatabaseCore database, EncryptionKeyWrapProvider encryptionKeyWrapProvider, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { Debug.Assert(input != null); Debug.Assert(database != null); Debug.Assert(input.CanSeek); Debug.Assert(diagnosticsContext != null); if (encryptionKeyWrapProvider == null) { return(input); } JObject itemJObj; using (StreamReader sr = new StreamReader(input, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true)) { using (JsonTextReader jsonTextReader = new JsonTextReader(sr)) { itemJObj = JsonSerializer.Create().Deserialize <JObject>(jsonTextReader); } } JProperty encryptionPropertiesJProp = itemJObj.Property(Constants.Properties.EncryptedInfo); JObject encryptionPropertiesJObj = null; if (encryptionPropertiesJProp != null && encryptionPropertiesJProp.Value != null && encryptionPropertiesJProp.Value.Type == JTokenType.Object) { encryptionPropertiesJObj = (JObject)encryptionPropertiesJProp.Value; } if (encryptionPropertiesJObj == null) { input.Position = 0; return(input); } EncryptionProperties encryptionProperties = encryptionPropertiesJObj.ToObject <EncryptionProperties>(); JObject plainTextJObj = await this.DecryptContentAsync( encryptionProperties, database, diagnosticsContext, cancellationToken); foreach (JProperty property in plainTextJObj.Properties()) { itemJObj.Add(property.Name, property.Value); } itemJObj.Remove(Constants.Properties.EncryptedInfo); return(EncryptionProcessor.baseSerializer.ToStream(itemJObj)); }
internal DatabaseInlineCore(DatabaseCore database) { if (database == null) { throw new ArgumentNullException(nameof(database)); } this.database = database; }
internal DataEncryptionKeyCore( CosmosClientContext clientContext, DatabaseCore database, string keyId) { this.Id = keyId; this.ClientContext = clientContext; this.LinkUri = DataEncryptionKeyCore.CreateLinkUri(clientContext, database, keyId); this.Database = database; }
/// <summary> /// <para>Check if a database exists, and if it doesn't, create it. /// Only the database id is used to verify if there is an existing database. Other database properties /// such as throughput are not validated and can be different then the passed properties.</para> /// /// <para>A database manages users, permissions and a set of containers. /// Each Azure Cosmos DB Database Account is able to support multiple independent named databases, /// with the database being the logical container for data.</para> /// /// <para>Each Database consists of one or more containers, each of which in turn contain one or more /// documents. Since databases are an administrative resource, the Service Master Key will be /// required in order to access and successfully complete any action using the User APIs.</para> /// </summary> /// <param name="id">The database id.</param> /// <param name="throughputProperties">The throughput provisioned for a database in measurement of Request Units per second in the Azure Cosmos DB service.</param> /// <param name="requestOptions">(Optional) A set of additional options that can be set.</param> /// <param name="cancellationToken">(Optional) <see cref="CancellationToken"/> representing request cancellation.</param> /// <returns>A <see cref="Task"/> containing a <see cref="DatabaseResponse"/> which wraps a <see cref="DatabaseProperties"/> containing the resource record. /// <list type="table"> /// <listheader> /// <term>StatusCode</term><description>Common success StatusCodes for the CreateDatabaseIfNotExistsAsync operation</description> /// </listheader> /// <item> /// <term>201</term><description>Created - New database is created.</description> /// </item> /// <item> /// <term>200</term><description>Accepted - This means the database already exists.</description> /// </item> /// </list> /// </returns> /// <exception>https://aka.ms/cosmosdb-dot-net-exceptions</exception> /// <seealso href="https://docs.microsoft.com/azure/cosmos-db/request-units">Request Units</seealso> public virtual Task <DatabaseResponse> CreateDatabaseIfNotExistsAsync( string id, ThroughputProperties throughputProperties, RequestOptions requestOptions = null, CancellationToken cancellationToken = default(CancellationToken)) { if (string.IsNullOrEmpty(id)) { throw new ArgumentNullException(nameof(id)); } return(this.ClientContext.OperationHelperAsync( nameof(CreateDatabaseIfNotExistsAsync), requestOptions, async(diagnostics) => { // Doing a Read before Create will give us better latency for existing databases DatabaseProperties databaseProperties = this.PrepareDatabaseProperties(id); DatabaseCore database = (DatabaseCore)this.GetDatabase(id); using (ResponseMessage readResponse = await database.ReadStreamAsync( diagnosticsContext: diagnostics, requestOptions: requestOptions, cancellationToken: cancellationToken)) { if (readResponse.StatusCode != HttpStatusCode.NotFound) { return this.ClientContext.ResponseFactory.CreateDatabaseResponse(database, readResponse); } } using (ResponseMessage createResponse = await this.CreateDatabaseStreamInternalAsync( diagnostics, databaseProperties, throughputProperties, requestOptions, cancellationToken)) { if (createResponse.StatusCode != HttpStatusCode.Conflict) { return this.ClientContext.ResponseFactory.CreateDatabaseResponse(this.GetDatabase(databaseProperties.Id), createResponse); } } // This second Read is to handle the race condition when 2 or more threads have Read the database and only one succeeds with Create // so for the remaining ones we should do a Read instead of throwing Conflict exception ResponseMessage readResponseAfterConflict = await database.ReadStreamAsync( diagnosticsContext: diagnostics, requestOptions: requestOptions, cancellationToken: cancellationToken); return this.ClientContext.ResponseFactory.CreateDatabaseResponse(this.GetDatabase(databaseProperties.Id), readResponseAfterConflict); })); }
internal UserCore( CosmosClientContext clientContext, DatabaseCore database, string userId) { this.Id = userId; this.ClientContext = clientContext; this.LinkUri = clientContext.CreateLink( parentLink: database.LinkUri.OriginalString, uriPathSegment: Paths.UsersPathSegment, id: userId); this.Database = database; }
public override async Task <ItemResponse <T> > ReadCurrentAsync <T>( ConflictProperties cosmosConflict, PartitionKey partitionKey, CancellationToken cancellationToken = default(CancellationToken)) { if (partitionKey == null) { throw new ArgumentNullException(nameof(partitionKey)); } if (cosmosConflict == null) { throw new ArgumentNullException(nameof(cosmosConflict)); } // SourceResourceId is RID based on Conflicts, so we need to obtain the db and container rid DatabaseCore databaseCore = (DatabaseCore)this.container.Database; string databaseResourceId = await databaseCore.GetRIDAsync(cancellationToken); string containerResourceId = await this.container.GetRIDAsync(cancellationToken); Uri dbLink = this.clientContext.CreateLink( parentLink: string.Empty, uriPathSegment: Paths.DatabasesPathSegment, id: databaseResourceId); Uri containerLink = this.clientContext.CreateLink( parentLink: dbLink.OriginalString, uriPathSegment: Paths.CollectionsPathSegment, id: containerResourceId); Uri itemLink = this.clientContext.CreateLink( parentLink: containerLink.OriginalString, uriPathSegment: Paths.DocumentsPathSegment, id: cosmosConflict.SourceResourceId); Task <ResponseMessage> response = this.clientContext.ProcessResourceOperationStreamAsync( resourceUri: itemLink, resourceType: ResourceType.Document, operationType: OperationType.Read, requestOptions: null, cosmosContainerCore: this.container, partitionKey: partitionKey, streamPayload: null, requestEnricher: null, cancellationToken: cancellationToken); return(await this.clientContext.ResponseFactory.CreateItemResponseAsync <T>(response)); }
public async Task EncryptionUTReadItem() { Container container = this.GetContainerWithMockSetup(); DatabaseCore database = (DatabaseCore)((ContainerCore)(ContainerInlineCore)container).Database; string dekId = "mydek"; DataEncryptionKeyResponse dekResponse = await database.CreateDataEncryptionKeyAsync(dekId, EncryptionUnitTests.Algo, this.metadata1); Assert.AreEqual(HttpStatusCode.Created, dekResponse.StatusCode); MyItem item = await EncryptionUnitTests.CreateItemAsync(container, dekId, MyItem.PathsToEncrypt); ItemResponse <MyItem> readResponse = await container.ReadItemAsync <MyItem>(item.Id, new PartitionKey(item.PK)); Assert.AreEqual(item, readResponse.Resource); }
public async Task EncryptionUTCreateDekWithoutEncryptionSerializer() { DatabaseCore database = (DatabaseCore)((ContainerCore)(ContainerInlineCore)this.GetContainer()).Database; try { await database.CreateDataEncryptionKeyAsync("mydek", EncryptionUnitTests.Algo, this.metadata1); Assert.Fail(); } catch (ArgumentException ex) { Assert.AreEqual(ClientResources.EncryptionKeyWrapProviderNotConfigured, ex.Message); } }
public async Task EncryptionUTRewrapDek() { Container container = this.GetContainerWithMockSetup(); DatabaseCore database = (DatabaseCore)((ContainerCore)(ContainerInlineCore)container).Database; string dekId = "mydek"; DataEncryptionKeyResponse createResponse = await database.CreateDataEncryptionKeyAsync(dekId, EncryptionUnitTests.Algo, this.metadata1); DataEncryptionKeyProperties createdProperties = createResponse.Resource; Assert.AreEqual(HttpStatusCode.Created, createResponse.StatusCode); this.VerifyWrap(this.dek, this.metadata1); DataEncryptionKey dek = database.GetDataEncryptionKey(dekId); DataEncryptionKeyResponse rewrapResponse = await dek.RewrapAsync(this.metadata2); DataEncryptionKeyProperties rewrappedProperties = rewrapResponse.Resource; Assert.IsNotNull(rewrappedProperties); Assert.AreEqual(dekId, rewrappedProperties.Id); Assert.AreEqual(createdProperties.CreatedTime, rewrappedProperties.CreatedTime); Assert.IsNotNull(rewrappedProperties.LastModified); Assert.AreEqual(createdProperties.ResourceId, rewrappedProperties.ResourceId); Assert.AreEqual(createdProperties.SelfLink, rewrappedProperties.SelfLink); IEnumerable <byte> expectedRewrappedKey = this.dek.Select(b => (byte)(b + 2)); Assert.IsTrue(expectedRewrappedKey.SequenceEqual(rewrappedProperties.WrappedDataEncryptionKey)); Assert.AreEqual(new EncryptionKeyWrapMetadata(this.metadata2.Value + this.metadataUpdateSuffix), rewrappedProperties.EncryptionKeyWrapMetadata); Assert.AreEqual(2, this.testHandler.Received.Count); RequestMessage rewrapRequestMessage = this.testHandler.Received[1]; Assert.AreEqual(ResourceType.ClientEncryptionKey, rewrapRequestMessage.ResourceType); Assert.AreEqual(OperationType.Replace, rewrapRequestMessage.OperationType); Assert.AreEqual(createResponse.ETag, rewrapRequestMessage.Headers[HttpConstants.HttpHeaders.IfMatch]); Assert.IsTrue(this.testHandler.Deks.ContainsKey(dekId)); DataEncryptionKeyProperties serverDekProperties = this.testHandler.Deks[dekId]; Assert.IsTrue(serverDekProperties.Equals(rewrappedProperties)); this.VerifyWrap(this.dek, this.metadata2); this.mockKeyWrapProvider.VerifyNoOtherCalls(); }
internal ContainerCore( CosmosClientContext clientContext, DatabaseCore database, string containerId) { this.Id = containerId; this.ClientContext = clientContext; this.LinkUri = clientContext.CreateLink( parentLink: database.LinkUri.OriginalString, uriPathSegment: Paths.CollectionsPathSegment, id: containerId); this.Database = database; this.conflicts = new ConflictsCore(this.ClientContext, this); this.scripts = new ScriptsCore(this, this.ClientContext); this.cachedUriSegmentWithoutId = this.GetResourceSegmentUriWithoutId(); this.queryClient = queryClient ?? new CosmosQueryClientCore(this.ClientContext, this); }
private static async Task <MyItem> CreateItemAsync(Container container, string dekId, List <string> pathsToEncrypt) { DatabaseCore database = (DatabaseCore)((ContainerCore)(ContainerInlineCore)container).Database; MyItem item = EncryptionUnitTests.GetNewItem(); ItemResponse <MyItem> response = await container.CreateItemAsync <MyItem>( item, requestOptions : new ItemRequestOptions { EncryptionOptions = new EncryptionOptions { DataEncryptionKey = database.GetDataEncryptionKey(dekId), PathsToEncrypt = pathsToEncrypt } }); Assert.AreEqual(HttpStatusCode.Created, response.StatusCode); Assert.AreEqual(item, response.Resource); return(item); }
public async Task <CosmosObject> DecryptAsync( CosmosObject document, DatabaseCore database, EncryptionKeyWrapProvider encryptionKeyWrapProvider, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { Debug.Assert(document != null); Debug.Assert(database != null); Debug.Assert(diagnosticsContext != null); if (encryptionKeyWrapProvider == null) { return(null); } if (!document.TryGetValue(Constants.Properties.EncryptedInfo, out CosmosElement encryptedInfo)) { return(document); } EncryptionProperties encryptionProperties = JsonConvert.DeserializeObject <EncryptionProperties>(encryptedInfo.ToString()); JObject plainTextJObj = await this.DecryptContentAsync( encryptionProperties, database, diagnosticsContext, cancellationToken); Dictionary <string, CosmosElement> documentContent = document.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); documentContent.Remove(Constants.Properties.EncryptedInfo); foreach (JProperty property in plainTextJObj.Properties()) { documentContent.Add(property.Name, property.Value.ToObject <CosmosElement>()); } return(CosmosObject.Create(documentContent)); }
internal override async Task <Stream> DecryptItemAsync( Stream input, DatabaseCore database, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { if (input == null || this.ClientOptions.Encryptor == null) { return(input); } Debug.Assert(database != null); Debug.Assert(diagnosticsContext != null); using (diagnosticsContext.CreateScope("Decrypt")) { return(await this.EncryptionProcessor.DecryptAsync( input, this.ClientOptions.Encryptor, diagnosticsContext, cancellationToken)); } }
public async Task EncryptionUTCreateItem() { Container container = this.GetContainerWithMockSetup(); DatabaseCore database = (DatabaseCore)((ContainerCore)(ContainerInlineCore)container).Database; string dekId = "mydek"; DataEncryptionKeyResponse dekResponse = await database.CreateDataEncryptionKeyAsync(dekId, EncryptionUnitTests.Algo, this.metadata1); Assert.AreEqual(HttpStatusCode.Created, dekResponse.StatusCode); MyItem item = await EncryptionUnitTests.CreateItemAsync(container, dekId, MyItem.PathsToEncrypt); // Validate server state Assert.IsTrue(this.testHandler.Items.TryGetValue(item.Id, out JObject serverItem)); Assert.IsNotNull(serverItem); Assert.AreEqual(item.Id, serverItem.Property(Constants.Properties.Id).Value.Value <string>()); Assert.AreEqual(item.PK, serverItem.Property(nameof(MyItem.PK)).Value.Value <string>()); Assert.IsNull(serverItem.Property(nameof(MyItem.EncStr1))); Assert.IsNull(serverItem.Property(nameof(MyItem.EncInt))); JProperty eiJProp = serverItem.Property(Constants.Properties.EncryptedInfo); Assert.IsNotNull(eiJProp); Assert.IsNotNull(eiJProp.Value); Assert.AreEqual(JTokenType.Object, eiJProp.Value.Type); EncryptionProperties encryptionPropertiesAtServer = ((JObject)eiJProp.Value).ToObject <EncryptionProperties>(); Assert.IsNotNull(encryptionPropertiesAtServer); Assert.AreEqual(dekResponse.Resource.ResourceId, encryptionPropertiesAtServer.DataEncryptionKeyRid); Assert.AreEqual(1, encryptionPropertiesAtServer.EncryptionFormatVersion); Assert.IsNotNull(encryptionPropertiesAtServer.EncryptedData); JObject decryptedJObj = EncryptionUnitTests.ParseStream(new MemoryStream(encryptionPropertiesAtServer.EncryptedData.Reverse().ToArray())); Assert.AreEqual(2, decryptedJObj.Properties().Count()); Assert.AreEqual(item.EncStr1, decryptedJObj.Property(nameof(MyItem.EncStr1)).Value.Value <string>()); Assert.AreEqual(item.EncInt, decryptedJObj.Property(nameof(MyItem.EncInt)).Value.Value <int>()); }
internal abstract Task <Stream> DecryptItemAsync( Stream input, DatabaseCore database, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken);
public async Task <Stream> DecryptAsync( Stream input, DatabaseCore database, EncryptionKeyWrapProvider encryptionKeyWrapProvider, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { Debug.Assert(input != null); Debug.Assert(database != null); Debug.Assert(input.CanSeek); Debug.Assert(diagnosticsContext != null); if (encryptionKeyWrapProvider == null) { return(input); } JObject itemJObj = EncryptionProcessor.baseSerializer.FromStream <JObject>(input); JProperty encryptionPropertiesJProp = itemJObj.Property(Constants.Properties.EncryptedInfo); JObject encryptionPropertiesJObj = null; if (encryptionPropertiesJProp != null && encryptionPropertiesJProp.Value != null && encryptionPropertiesJProp.Value.Type == JTokenType.Object) { encryptionPropertiesJObj = (JObject)encryptionPropertiesJProp.Value; } if (encryptionPropertiesJObj == null) { input.Position = 0; return(input); } EncryptionProperties encryptionProperties = encryptionPropertiesJObj.ToObject <EncryptionProperties>(); if (encryptionProperties.EncryptionFormatVersion != 1) { throw CosmosExceptionFactory.CreateInternalServerErrorException($"Unknown encryption format version: {encryptionProperties.EncryptionFormatVersion}. Please upgrade your SDK to the latest version."); } DataEncryptionKeyCore tempDek = (DataEncryptionKeyInlineCore)database.GetDataEncryptionKey(id: "unknown"); (DataEncryptionKeyProperties _, InMemoryRawDek inMemoryRawDek) = await tempDek.FetchUnwrappedByRidAsync( encryptionProperties.DataEncryptionKeyRid, diagnosticsContext, cancellationToken); byte[] plainText = inMemoryRawDek.AlgorithmUsingRawDek.DecryptData(encryptionProperties.EncryptedData); JObject plainTextJObj = null; using (MemoryStream memoryStream = new MemoryStream(plainText)) using (StreamReader streamReader = new StreamReader(memoryStream)) using (JsonTextReader jsonTextReader = new JsonTextReader(streamReader)) { plainTextJObj = JObject.Load(jsonTextReader); } foreach (JProperty property in plainTextJObj.Properties()) { itemJObj.Add(property.Name, property.Value); } itemJObj.Remove(Constants.Properties.EncryptedInfo); return(EncryptionProcessor.baseSerializer.ToStream(itemJObj)); }
public async Task EncryptionUTCreateDek() { Container container = this.GetContainerWithMockSetup(); DatabaseCore database = (DatabaseCore)((ContainerCore)(ContainerInlineCore)container).Database; string dekId = "mydek"; DataEncryptionKeyResponse dekResponse = await database.CreateDataEncryptionKeyAsync(dekId, EncryptionUnitTests.Algo, this.metadata1); Assert.AreEqual(HttpStatusCode.Created, dekResponse.StatusCode); Assert.AreEqual(requestCharge, dekResponse.RequestCharge); Assert.IsNotNull(dekResponse.ETag); DataEncryptionKeyProperties dekProperties = dekResponse.Resource; Assert.IsNotNull(dekProperties); Assert.AreEqual(dekResponse.ETag, dekProperties.ETag); Assert.AreEqual(dekId, dekProperties.Id); Assert.AreEqual(1, this.testHandler.Received.Count); RequestMessage createDekRequestMessage = this.testHandler.Received[0]; Assert.AreEqual(ResourceType.ClientEncryptionKey, createDekRequestMessage.ResourceType); Assert.AreEqual(OperationType.Create, createDekRequestMessage.OperationType); Assert.IsTrue(this.testHandler.Deks.ContainsKey(dekId)); DataEncryptionKeyProperties serverDekProperties = this.testHandler.Deks[dekId]; Assert.IsTrue(serverDekProperties.Equals(dekProperties)); // Make sure we didn't push anything else in the JSON (such as raw DEK) by comparing JSON properties // to properties exposed in DataEncryptionKeyProperties. createDekRequestMessage.Content.Position = 0; // it is a test assumption that the client uses MemoryStream JObject jObj = JObject.Parse(await new StreamReader(createDekRequestMessage.Content).ReadToEndAsync()); IEnumerable <string> dekPropertiesPropertyNames = GetJsonPropertyNamesForType(typeof(DataEncryptionKeyProperties)); foreach (JProperty property in jObj.Properties()) { Assert.IsTrue(dekPropertiesPropertyNames.Contains(property.Name)); } // Key wrap metadata should be the only "object" child in the JSON (given current properties in DataEncryptionKeyProperties) IEnumerable <JToken> objectChildren = jObj.PropertyValues().Where(v => v.Type == JTokenType.Object); Assert.AreEqual(1, objectChildren.Count()); JObject keyWrapMetadataJObj = (JObject)objectChildren.First(); Assert.AreEqual(Constants.Properties.KeyWrapMetadata, ((JProperty)keyWrapMetadataJObj.Parent).Name); IEnumerable <string> keyWrapMetadataPropertyNames = GetJsonPropertyNamesForType(typeof(EncryptionKeyWrapMetadata)); foreach (JProperty property in keyWrapMetadataJObj.Properties()) { Assert.IsTrue(keyWrapMetadataPropertyNames.Contains(property.Name)); } IEnumerable <byte> expectedWrappedKey = this.VerifyWrap(this.dek, this.metadata1); this.mockKeyWrapProvider.VerifyNoOtherCalls(); Assert.IsTrue(expectedWrappedKey.SequenceEqual(dekProperties.WrappedDataEncryptionKey)); }
public async Task <Stream> EncryptAsync( Stream input, EncryptionOptions encryptionOptions, DatabaseCore database, EncryptionKeyWrapProvider encryptionKeyWrapProvider, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { Debug.Assert(input != null); Debug.Assert(encryptionOptions != null); Debug.Assert(database != null); Debug.Assert(diagnosticsContext != null); if (encryptionOptions.PathsToEncrypt == null) { throw new ArgumentNullException(nameof(encryptionOptions.PathsToEncrypt)); } if (encryptionOptions.PathsToEncrypt.Count == 0) { return(input); } foreach (string path in encryptionOptions.PathsToEncrypt) { if (string.IsNullOrEmpty(path) || path[0] != '/' || path.LastIndexOf('/') != 0) { throw new ArgumentException($"Invalid path {path ?? string.Empty}", nameof(encryptionOptions.PathsToEncrypt)); } } if (encryptionOptions.DataEncryptionKey == null) { throw new ArgumentException("Invalid encryption options", nameof(encryptionOptions.DataEncryptionKey)); } if (encryptionKeyWrapProvider == null) { throw new ArgumentException(ClientResources.EncryptionKeyWrapProviderNotConfigured); } DataEncryptionKey dek = database.GetDataEncryptionKey(encryptionOptions.DataEncryptionKey.Id); DataEncryptionKeyCore dekCore = (DataEncryptionKeyInlineCore)dek; (DataEncryptionKeyProperties dekProperties, InMemoryRawDek inMemoryRawDek) = await dekCore.FetchUnwrappedAsync( diagnosticsContext, cancellationToken); JObject itemJObj = EncryptionProcessor.baseSerializer.FromStream <JObject>(input); JObject toEncryptJObj = new JObject(); foreach (string pathToEncrypt in encryptionOptions.PathsToEncrypt) { string propertyName = pathToEncrypt.Substring(1); JToken propertyValueHolder = itemJObj.Property(propertyName).Value; // Even null in the JSON is a JToken with Type Null, this null check is just a sanity check if (propertyValueHolder != null) { toEncryptJObj.Add(propertyName, itemJObj.Property(propertyName).Value.Value <JToken>()); itemJObj.Remove(propertyName); } } MemoryStream memoryStream = EncryptionProcessor.baseSerializer.ToStream <JObject>(toEncryptJObj) as MemoryStream; Debug.Assert(memoryStream != null); Debug.Assert(memoryStream.TryGetBuffer(out _)); byte[] plainText = memoryStream.GetBuffer(); EncryptionProperties encryptionProperties = new EncryptionProperties( dataEncryptionKeyRid: dekProperties.ResourceId, encryptionFormatVersion: 1, encryptedData: inMemoryRawDek.AlgorithmUsingRawDek.EncryptData(plainText)); itemJObj.Add(Constants.Properties.EncryptedInfo, JObject.FromObject(encryptionProperties)); return(EncryptionProcessor.baseSerializer.ToStream(itemJObj)); }