Example #1
0
        /// <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,
            CosmosDiagnosticsContext diagnosticsContext,
            CancellationToken cancellationToken)
        {
            if (input == null)
            {
                throw new ArgumentNullException(nameof(input));
            }

            Debug.Assert(diagnosticsContext != null);

            JObject itemJObj = EncryptionProcessor.BaseSerializer.FromStream <JObject>(input);

            foreach (string propertyName in encryptionSettings.PropertiesToEncrypt)
            {
                // possibly a wrong path configured in the Client Encryption Policy, ignore.
                if (!itemJObj.TryGetValue(propertyName, out JToken propertyValue))
                {
                    continue;
                }

                EncryptionSettingForProperty settingforProperty = encryptionSettings.GetEncryptionSettingForProperty(propertyName);

                if (settingforProperty == null)
                {
                    throw new ArgumentException($"Invalid Encryption Setting for the Property:{propertyName}. ");
                }

                AeadAes256CbcHmac256EncryptionAlgorithm aeadAes256CbcHmac256EncryptionAlgorithm = await settingforProperty.BuildEncryptionAlgorithmForSettingAsync(cancellationToken : cancellationToken);

                EncryptProperty(
                    itemJObj,
                    propertyValue,
                    aeadAes256CbcHmac256EncryptionAlgorithm);
            }

            input.Dispose();
            return(EncryptionProcessor.BaseSerializer.ToStream(itemJObj));
        }
Example #2
0
        internal static async Task <Stream> EncryptValueStreamAsync(
            Stream valueStream,
            EncryptionSettingForProperty settingsForProperty,
            CancellationToken cancellationToken)
        {
            if (valueStream == null)
            {
                throw new ArgumentNullException(nameof(valueStream));
            }

            if (settingsForProperty == null)
            {
                throw new ArgumentNullException(nameof(settingsForProperty));
            }

            AeadAes256CbcHmac256EncryptionAlgorithm aeadAes256CbcHmac256EncryptionAlgorithm = await settingsForProperty.BuildEncryptionAlgorithmForSettingAsync(cancellationToken : cancellationToken);

            JToken propertyValueToEncrypt = EncryptionProcessor.BaseSerializer.FromStream <JToken>(valueStream);

            (EncryptionProcessor.TypeMarker typeMarker, byte[] serializedData) = EncryptionProcessor.Serialize(propertyValueToEncrypt);

            byte[] cipherText = aeadAes256CbcHmac256EncryptionAlgorithm.Encrypt(serializedData);

            if (cipherText == null)
            {
                throw new InvalidOperationException($"{nameof(EncryptValueStreamAsync)} returned null cipherText from {nameof(aeadAes256CbcHmac256EncryptionAlgorithm.Encrypt)}. Please refer to https://aka.ms/CosmosClientEncryption for more details. ");
            }

            byte[] cipherTextWithTypeMarker = new byte[cipherText.Length + 1];
            cipherTextWithTypeMarker[0] = (byte)typeMarker;
            Buffer.BlockCopy(cipherText, 0, cipherTextWithTypeMarker, 1, cipherText.Length);

            return(EncryptionProcessor.BaseSerializer.ToStream(cipherTextWithTypeMarker));
        }
Example #3
0
        private async Task DecryptObjectAsync(
            JObject document,
            CosmosDiagnosticsContext diagnosticsContext,
            CancellationToken cancellationToken)
        {
            Debug.Assert(diagnosticsContext != null);

            foreach (ClientEncryptionIncludedPath path in this.ClientEncryptionPolicy.IncludedPaths)
            {
                if (document.TryGetValue(path.Path.Substring(1), out JToken propertyValue))
                {
                    string propertyName = path.Path.Substring(1);
                    EncryptionSettingForProperty settingsForProperty = await this.EncryptionSettings.GetEncryptionSettingForPropertyAsync(propertyName, cancellationToken);

                    if (settingsForProperty == null)
                    {
                        throw new ArgumentException($"Invalid Encryption Setting for Property:{propertyName}. ");
                    }

                    AeadAes256CbcHmac256EncryptionAlgorithm aeadAes256CbcHmac256EncryptionAlgorithm = await settingsForProperty.BuildEncryptionAlgorithmForSettingAsync(cancellationToken : cancellationToken);

                    this.DecryptProperty(
                        document,
                        aeadAes256CbcHmac256EncryptionAlgorithm,
                        propertyName,
                        propertyValue);
                }
            }

            return;
        }
Example #4
0
        /// <summary>
        /// Gets a QueryDefinition with Encrypted Parameters.
        /// </summary>
        /// <param name="queryDefinition"> Query Definition to be replaced with Encrypted Values.</param>
        /// <param name="name"> Query Paramerter Name. </param>
        /// <param name="value"> Query Paramerter Value.</param>
        /// <param name="path"> Encrypted Property Path. </param>
        /// <param name="cancellationToken"> cancellation token </param>
        /// <returns> QueryDefinition with encrypted parameters. </returns>
        /// <example>
        /// This example shows how to pass in a QueryDefinition with Encryption support to AddParameterAsync
        /// to encrypt the required Property for running Query on encrypted data.
        ///
        /// <code language="c#">
        /// <![CDATA[
        /// containerWithEncryption = await this.cosmosDatabase.GetContainer("id").InitializeEncryptionAsync();
        /// QueryDefinition withEncryptedParameter = containerWithEncryption.CreateQueryDefinition(
        ///     "SELECT * FROM c where c.PropertyName = @PropertyValue");
        /// await withEncryptedParameter.AddParameterAsync(
        ///     "@PropertyName",
        ///     PropertyValue,
        ///     "/PropertyName");
        /// ]]>
        /// </code>
        /// </example>
        public static async Task <QueryDefinition> AddParameterAsync(
            this QueryDefinition queryDefinition,
            string name,
            object value,
            string path,
            CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (queryDefinition == null)
            {
                throw new ArgumentNullException(nameof(queryDefinition));
            }

            if (string.IsNullOrWhiteSpace(path) || path[0] != '/' || path.LastIndexOf('/') != 0)
            {
                throw new InvalidOperationException($"Invalid path {path ?? string.Empty}, {nameof(path)}. ");
            }

            if (string.IsNullOrWhiteSpace(name))
            {
                throw new ArgumentNullException(nameof(name));
            }

            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }

            QueryDefinition queryDefinitionwithEncryptedValues = queryDefinition;

            if (queryDefinition is EncryptionQueryDefinition encryptionQueryDefinition)
            {
                EncryptionContainer encryptionContainer = (EncryptionContainer)encryptionQueryDefinition.Container;
                Stream valueStream = encryptionContainer.CosmosSerializer.ToStream(value);

                // not really required, but will have things setup for subsequent queries or operations on this Container if the Container was never init
                // or if this was the first operation carried out on this container.
                await encryptionContainer.EncryptionProcessor.InitEncryptionSettingsIfNotInitializedAsync(cancellationToken);

                // get the path's encryption setting.
                EncryptionSettingForProperty settingsForProperty = await encryptionContainer.EncryptionProcessor.EncryptionSettings.GetEncryptionSettingForPropertyAsync(
                    path.Substring(1),
                    cancellationToken);

                if (settingsForProperty == null)
                {
                    // property not encrypted.
                    queryDefinitionwithEncryptedValues.WithParameter(name, value);
                    return(queryDefinitionwithEncryptedValues);
                }

                if (settingsForProperty.EncryptionType == EncryptionType.Randomized)
                {
                    throw new ArgumentException($"Unsupported argument with Path: {path} for query. For executing queries on encrypted path requires the use of deterministic encryption type. Please refer to https://aka.ms/CosmosClientEncryption for more details. ");
                }

                AeadAes256CbcHmac256EncryptionAlgorithm aeadAes256CbcHmac256EncryptionAlgorithm = await settingsForProperty.BuildEncryptionAlgorithmForSettingAsync(cancellationToken : cancellationToken);

                JToken propertyValueToEncrypt = EncryptionProcessor.BaseSerializer.FromStream <JToken>(valueStream);
                (EncryptionProcessor.TypeMarker typeMarker, byte[] serializedData) = EncryptionProcessor.Serialize(propertyValueToEncrypt);

                byte[] cipherText = aeadAes256CbcHmac256EncryptionAlgorithm.Encrypt(serializedData);

                if (cipherText == null)
                {
                    throw new InvalidOperationException($"{nameof(AddParameterAsync)} returned null cipherText from {nameof(aeadAes256CbcHmac256EncryptionAlgorithm.Encrypt)}. Please refer to https://aka.ms/CosmosClientEncryption for more details. ");
                }

                byte[] cipherTextWithTypeMarker = new byte[cipherText.Length + 1];
                cipherTextWithTypeMarker[0] = (byte)typeMarker;
                Buffer.BlockCopy(cipherText, 0, cipherTextWithTypeMarker, 1, cipherText.Length);
                queryDefinitionwithEncryptedValues.WithParameter(name, cipherTextWithTypeMarker);

                return(queryDefinitionwithEncryptedValues);
            }
            else
            {
                throw new ArgumentException("Executing queries on encrypted path requires the use of an encryption - enabled client. Please refer to https://aka.ms/CosmosClientEncryption for more details. ");
            }
        }
Example #5
0
        /// <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 async Task <Stream> EncryptAsync(
            Stream input,
            CosmosDiagnosticsContext diagnosticsContext,
            CancellationToken cancellationToken)
        {
            if (input == null)
            {
                throw new ArgumentNullException(nameof(input));
            }

            Debug.Assert(diagnosticsContext != null);

            await this.InitEncryptionSettingsIfNotInitializedAsync(cancellationToken);

            if (this.ClientEncryptionPolicy == null)
            {
                return(input);
            }

            JObject itemJObj = EncryptionProcessor.BaseSerializer.FromStream <JObject>(input);

            foreach (ClientEncryptionIncludedPath pathToEncrypt in this.ClientEncryptionPolicy.IncludedPaths)
            {
                string propertyName = pathToEncrypt.Path.Substring(1);

                // possibly a wrong path configured in the Client Encryption Policy, ignore.
                if (!itemJObj.TryGetValue(propertyName, out JToken propertyValue))
                {
                    continue;
                }

                EncryptionSettingForProperty settingforProperty = await this.EncryptionSettings.GetEncryptionSettingForPropertyAsync(propertyName, cancellationToken);

                if (settingforProperty == null)
                {
                    throw new ArgumentException($"Invalid Encryption Setting for the Property:{propertyName}. ");
                }

                AeadAes256CbcHmac256EncryptionAlgorithm aeadAes256CbcHmac256EncryptionAlgorithm = await settingforProperty.BuildEncryptionAlgorithmForSettingAsync(cancellationToken : cancellationToken);

                this.EncryptProperty(
                    itemJObj,
                    propertyValue,
                    aeadAes256CbcHmac256EncryptionAlgorithm);
            }

            input.Dispose();
            return(EncryptionProcessor.BaseSerializer.ToStream(itemJObj));
        }