public override async Task <ResponseMessage> ReadNextAsync(CancellationToken cancellationToken = default) { EncryptionSettings encryptionSettings = await this.encryptionContainer.GetOrUpdateEncryptionSettingsFromCacheAsync(obsoleteEncryptionSettings : null, cancellationToken : cancellationToken); await this.GetIteratorWithEncryptionHeaderAndEncryptPartitionKeyIfRequiredAsync(encryptionSettings); ResponseMessage responseMessage = await this.FeedIterator.ReadNextAsync(cancellationToken); EncryptionDiagnosticsContext encryptionDiagnosticsContext = new EncryptionDiagnosticsContext(); // check for Bad Request and Wrong intended RID and update the cached RID and Client Encryption Policy. await this.encryptionContainer.ThrowIfRequestNeedsARetryPostPolicyRefreshAsync(responseMessage, encryptionSettings, encryptionDiagnosticsContext, cancellationToken); if (responseMessage.IsSuccessStatusCode && responseMessage.Content != null) { Stream decryptedContent = await EncryptionProcessor.DeserializeAndDecryptResponseAsync( responseMessage.Content, encryptionSettings, encryptionDiagnosticsContext, cancellationToken); encryptionDiagnosticsContext.AddEncryptionDiagnosticsToResponseMessage(responseMessage); return(new DecryptedResponseMessage(responseMessage, decryptedContent)); } return(responseMessage); }
/// <remarks> /// If there isn't any data that needs to be decrypted, input stream will be returned without any modification. /// Else input stream will be disposed, and a new stream is returned. /// In case of an exception, input stream won't be disposed, but position will be end of stream. /// </remarks> public static async Task <Stream> DecryptAsync( Stream input, EncryptionSettings encryptionSettings, EncryptionDiagnosticsContext operationDiagnostics, CancellationToken cancellationToken) { if (input == null) { return(input); } Debug.Assert(input.CanSeek, "DecryptAsync input.CanSeek false"); operationDiagnostics?.Begin(Constants.DiagnosticsDecryptOperation); JObject itemJObj = RetrieveItem(input); int propertiesDecryptedCount = await DecryptObjectAsync( itemJObj, encryptionSettings, cancellationToken); Stream result = EncryptionProcessor.BaseSerializer.ToStream(itemJObj); input.Dispose(); operationDiagnostics?.End(propertiesDecryptedCount); return(result); }
public EncryptionTransactionalBatch( TransactionalBatch transactionalBatch, EncryptionContainer encryptionContainer, CosmosSerializer cosmosSerializer) { this.transactionalBatch = transactionalBatch ?? throw new ArgumentNullException(nameof(transactionalBatch)); this.encryptionContainer = encryptionContainer ?? throw new ArgumentNullException(nameof(encryptionContainer)); this.cosmosSerializer = cosmosSerializer ?? throw new ArgumentNullException(nameof(cosmosSerializer)); this.encryptionDiagnosticsContext = new EncryptionDiagnosticsContext(); this.encryptionDiagnosticsContext.Begin(Constants.DiagnosticsEncryptOperation); }
/// <remarks> /// If there isn't any PathsToEncrypt, input stream will be returned without any modification. /// Else input stream will be disposed, and a new stream is returned. /// In case of an exception, input stream won't be disposed, but position will be end of stream. /// </remarks> public static async Task <Stream> EncryptAsync( Stream input, EncryptionSettings encryptionSettings, EncryptionDiagnosticsContext operationDiagnostics, CancellationToken cancellationToken) { if (input == null) { throw new ArgumentNullException(nameof(input)); } operationDiagnostics?.Begin(Constants.DiagnosticsEncryptOperation); int propertiesEncryptedCount = 0; JObject itemJObj = EncryptionProcessor.BaseSerializer.FromStream <JObject>(input); foreach (string propertyName in encryptionSettings.PropertiesToEncrypt) { // possibly a wrong path configured in the Client Encryption Policy, ignore. JProperty propertyToEncrypt = itemJObj.Property(propertyName); if (propertyToEncrypt == null) { continue; } EncryptionSettingForProperty settingforProperty = encryptionSettings.GetEncryptionSettingForProperty(propertyName); if (settingforProperty == null) { throw new ArgumentException($"Invalid Encryption Setting for the Property:{propertyName}. "); } await EncryptJTokenAsync( propertyToEncrypt.Value, settingforProperty, propertyName == "id", cancellationToken); propertiesEncryptedCount++; } Stream result = EncryptionProcessor.BaseSerializer.ToStream(itemJObj); input.Dispose(); operationDiagnostics?.End(propertiesEncryptedCount); return(result); }
public override async Task <ResponseMessage> ReadNextAsync(CancellationToken cancellationToken = default) { EncryptionSettings encryptionSettings = await this.encryptionContainer.GetOrUpdateEncryptionSettingsFromCacheAsync(obsoleteEncryptionSettings : null, cancellationToken : cancellationToken); encryptionSettings.SetRequestHeaders(this.requestOptions); ResponseMessage responseMessage = await this.feedIterator.ReadNextAsync(cancellationToken); // check for Bad Request and Wrong RID intended and update the cached RID and Client Encryption Policy. if (responseMessage.StatusCode == HttpStatusCode.BadRequest && string.Equals(responseMessage.Headers.Get(Constants.SubStatusHeader), Constants.IncorrectContainerRidSubStatus)) { await this.encryptionContainer.GetOrUpdateEncryptionSettingsFromCacheAsync( obsoleteEncryptionSettings : encryptionSettings, cancellationToken : cancellationToken); throw new CosmosException( "Operation has failed due to a possible mismatch in Client Encryption Policy configured on the container. Please refer to https://aka.ms/CosmosClientEncryption for more details. " + responseMessage.ErrorMessage, responseMessage.StatusCode, int.Parse(Constants.IncorrectContainerRidSubStatus), responseMessage.Headers.ActivityId, responseMessage.Headers.RequestCharge); } if (responseMessage.IsSuccessStatusCode && responseMessage.Content != null) { EncryptionDiagnosticsContext decryptDiagnostics = new EncryptionDiagnosticsContext(); Stream decryptedContent = await EncryptionProcessor.DeserializeAndDecryptResponseAsync( responseMessage.Content, encryptionSettings, decryptDiagnostics, cancellationToken); decryptDiagnostics.AddEncryptionDiagnosticsToResponseMessage(responseMessage); return(new DecryptedResponseMessage(responseMessage, decryptedContent)); } return(responseMessage); }
internal static async Task <Stream> DeserializeAndDecryptResponseAsync( Stream content, EncryptionSettings encryptionSettings, EncryptionDiagnosticsContext operationDiagnostics, CancellationToken cancellationToken) { if (!encryptionSettings.PropertiesToEncrypt.Any()) { return(content); } operationDiagnostics?.Begin(Constants.DiagnosticsDecryptOperation); JObject contentJObj = EncryptionProcessor.BaseSerializer.FromStream <JObject>(content); if (!(contentJObj.SelectToken(Constants.DocumentsResourcePropertyName) is JArray documents)) { throw new InvalidOperationException("Feed Response body contract was violated. Feed response did not have an array of Documents. "); } int totalPropertiesDecryptedCount = 0; foreach (JToken value in documents) { if (value is not JObject document) { continue; } (_, int propertiesDecrypted) = await EncryptionProcessor.DecryptAsync( document, encryptionSettings, cancellationToken); totalPropertiesDecryptedCount += propertiesDecrypted; } operationDiagnostics?.End(totalPropertiesDecryptedCount); // the contents get decrypted in place by DecryptAsync. return(EncryptionProcessor.BaseSerializer.ToStream(contentJObj)); }