private async Task <ClientEncryptionKeyProperties> FetchClientEncryptionKeyPropertiesAsync(
            EncryptionContainer encryptionContainer,
            string clientEncryptionKeyId,
            RequestOptions requestOptions,
            CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            ClientEncryptionKey clientEncryptionKey = encryptionContainer.Database.GetClientEncryptionKey(clientEncryptionKeyId);

            try
            {
                return(await clientEncryptionKey.ReadAsync(requestOptions : requestOptions, cancellationToken : cancellationToken));
            }
            catch (CosmosException ex)
            {
                if (ex.StatusCode == HttpStatusCode.NotFound)
                {
                    throw new InvalidOperationException($"Encryption Based Container without Client Encryption Keys. Please make sure you have created the Client Encryption Keys:{ex.Message}. Please refer to https://aka.ms/CosmosClientEncryption for more details.");
                }
                else
                {
                    throw;
                }
            }
        }
 public EncryptionContainerResponse(
     ContainerResponse containerResponse,
     EncryptionContainer encryptionContainer)
 {
     this.containerResponse   = containerResponse ?? throw new ArgumentNullException(nameof(containerResponse));
     this.encryptionContainer = encryptionContainer ?? throw new ArgumentNullException(nameof(encryptionContainer));
 }
        public async Task <ClientEncryptionKeyProperties> GetClientEncryptionKeyPropertiesAsync(
            string clientEncryptionKeyId,
            EncryptionContainer encryptionContainer,
            string databaseRid,
            CancellationToken cancellationToken = default,
            bool shouldForceRefresh             = false)
        {
            if (encryptionContainer == null)
            {
                throw new ArgumentNullException(nameof(encryptionContainer));
            }

            if (string.IsNullOrEmpty(databaseRid))
            {
                throw new ArgumentNullException(nameof(databaseRid));
            }

            if (string.IsNullOrEmpty(clientEncryptionKeyId))
            {
                throw new ArgumentNullException(nameof(clientEncryptionKeyId));
            }

            // Client Encryption key Id is unique within a Database.
            string cacheKey = databaseRid + "|" + clientEncryptionKeyId;

            return(await this.clientEncryptionKeyPropertiesCacheByKeyId.GetAsync(
                       cacheKey,
                       obsoleteValue : null,
                       async() => await this.FetchClientEncryptionKeyPropertiesAsync(encryptionContainer, clientEncryptionKeyId, cancellationToken),
                       cancellationToken,
                       forceRefresh : shouldForceRefresh));
        }
Esempio n. 4
0
 public EncryptionFeedIterator(
     FeedIterator feedIterator,
     EncryptionContainer encryptionContainer,
     RequestOptions requestOptions)
 {
     this.feedIterator        = feedIterator ?? throw new ArgumentNullException(nameof(feedIterator));
     this.encryptionContainer = encryptionContainer ?? throw new ArgumentNullException(nameof(encryptionContainer));
     this.requestOptions      = requestOptions ?? throw new ArgumentNullException(nameof(requestOptions));
 }
Esempio n. 5
0
 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));
 }
 public EncryptionSettingForProperty(
     string clientEncryptionKeyId,
     Data.Encryption.Cryptography.EncryptionType encryptionType,
     EncryptionContainer encryptionContainer,
     string databaseRid)
 {
     this.ClientEncryptionKeyId = string.IsNullOrEmpty(clientEncryptionKeyId) ? throw new ArgumentNullException(nameof(clientEncryptionKeyId)) : clientEncryptionKeyId;
     this.EncryptionType        = encryptionType;
     this.encryptionContainer   = encryptionContainer ?? throw new ArgumentNullException(nameof(encryptionContainer));
     this.databaseRid           = string.IsNullOrEmpty(databaseRid) ? throw new ArgumentNullException(nameof(databaseRid)) : databaseRid;
 }
 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);
 }
 public EncryptionFeedIterator(
     EncryptionContainer encryptionContainer,
     ChangeFeedStartFrom changeFeedStartFrom,
     ChangeFeedMode changeFeedMode,
     ChangeFeedRequestOptions changeFeedRequestOptions = null)
 {
     this.encryptionContainer = encryptionContainer ?? throw new ArgumentNullException(nameof(encryptionContainer));
     this.requestOptions      = changeFeedRequestOptions;
     this.FeedIterator        = encryptionContainer.Container.GetChangeFeedStreamIterator(
         changeFeedStartFrom,
         changeFeedMode,
         changeFeedRequestOptions);
 }
        public async Task <ClientEncryptionKeyProperties> GetClientEncryptionKeyPropertiesAsync(
            string clientEncryptionKeyId,
            EncryptionContainer encryptionContainer,
            string databaseRid,
            string ifNoneMatchEtag,
            bool shouldForceRefresh,
            CancellationToken cancellationToken)
        {
            if (encryptionContainer == null)
            {
                throw new ArgumentNullException(nameof(encryptionContainer));
            }

            if (string.IsNullOrEmpty(databaseRid))
            {
                throw new ArgumentNullException(nameof(databaseRid));
            }

            if (string.IsNullOrEmpty(clientEncryptionKeyId))
            {
                throw new ArgumentNullException(nameof(clientEncryptionKeyId));
            }

            // Client Encryption key Id is unique within a Database.
            string cacheKey = databaseRid + "|" + clientEncryptionKeyId;

            // this allows us to read from the Gateway Cache. If an IfNoneMatchEtag is passed the logic around the gateway cache allows us to fetch the latest ClientEncryptionKeyProperties
            // from the servers if the gateway cache has a stale value. This can happen if a client connected via different Gateway has rewrapped the key.
            RequestOptions requestOptions = new RequestOptions
            {
                AddRequestHeaders = (headers) =>
                {
                    headers.Add(Constants.AllowCachedReadsHeader, bool.TrueString);
                    headers.Add(Constants.DatabaseRidHeader, databaseRid);
                },
            };

            if (!string.IsNullOrEmpty(ifNoneMatchEtag))
            {
                requestOptions.IfNoneMatchEtag = ifNoneMatchEtag;
            }

            return(await this.clientEncryptionKeyPropertiesCacheByKeyId.GetAsync(
                       cacheKey,
                       obsoleteValue : null,
                       async() => await this.FetchClientEncryptionKeyPropertiesAsync(encryptionContainer, clientEncryptionKeyId, requestOptions, cancellationToken),
                       cancellationToken,
                       forceRefresh : shouldForceRefresh));
        }
        public EncryptionFeedIterator(
            EncryptionContainer encryptionContainer,
            string queryText                   = null,
            string continuationToken           = null,
            QueryRequestOptions requestOptions = null)
        {
            this.encryptionContainer = encryptionContainer ?? throw new ArgumentNullException(nameof(encryptionContainer));
            this.queryText           = queryText;
            this.continuationToken   = continuationToken;
            this.requestOptions      = requestOptions;

            this.FeedIterator = encryptionContainer.Container.GetItemQueryStreamIterator(
                queryText,
                continuationToken,
                requestOptions);

            this.queryType = QueryType.QueryTextType;
        }
        public EncryptionFeedIterator(
            EncryptionContainer encryptionContainer,
            FeedRange feedRange,
            QueryDefinition queryDefinition,
            string continuationToken,
            QueryRequestOptions requestOptions = null)
        {
            this.encryptionContainer = encryptionContainer ?? throw new ArgumentNullException(nameof(encryptionContainer));
            this.feedRange           = feedRange;
            this.queryDefinition     = queryDefinition;
            this.continuationToken   = continuationToken;
            this.requestOptions      = requestOptions;

            this.FeedIterator = encryptionContainer.Container.GetItemQueryStreamIterator(
                feedRange,
                queryDefinition,
                continuationToken,
                requestOptions);

            this.queryType = QueryType.QueryDefinitionWithFeedRangeType;
        }
Esempio n. 12
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;

                // get the path's encryption setting.
                EncryptionSettings encryptionSettings = await encryptionContainer.GetOrUpdateEncryptionSettingsFromCacheAsync(obsoleteEncryptionSettings : null, cancellationToken : cancellationToken);

                EncryptionSettingForProperty settingsForProperty = encryptionSettings.GetEncryptionSettingForProperty(path.Substring(1));

                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. ");
                }

                Stream valueStream          = encryptionContainer.CosmosSerializer.ToStream(value);
                Stream encryptedValueStream = await EncryptionProcessor.EncryptValueStreamAsync(valueStream, settingsForProperty, cancellationToken);

                queryDefinitionwithEncryptedValues.WithParameterStream(name, encryptedValueStream);

                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. ");
            }
        }
 public static Task <EncryptionSettings> CreateAsync(EncryptionContainer encryptionContainer, CancellationToken cancellationToken)
 {
     return(InitializeEncryptionSettingsAsync(encryptionContainer, cancellationToken));
 }
Esempio n. 14
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. ");
            }
        }