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);
        }
Пример #5
0
        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));
        }