Beispiel #1
0
        internal async Task <(byte[], EncryptionKeyWrapMetadata, InMemoryRawDek)> WrapAsync(
            byte[] key,
            CosmosEncryptionAlgorithm encryptionAlgorithmId,
            EncryptionKeyWrapMetadata metadata,
            CosmosDiagnosticsContext diagnosticsContext,
            CancellationToken cancellationToken)
        {
            EncryptionKeyWrapProvider encryptionKeyWrapProvider = this.ClientContext.ClientOptions.EncryptionKeyWrapProvider;

            if (encryptionKeyWrapProvider == null)
            {
                throw new ArgumentException(ClientResources.EncryptionKeyWrapProviderNotConfigured);
            }

            EncryptionKeyWrapResult keyWrapResponse;

            using (diagnosticsContext.CreateScope("WrapDataEncryptionKey"))
            {
                keyWrapResponse = await encryptionKeyWrapProvider.WrapKeyAsync(key, metadata, cancellationToken);
            }

            // Verify
            DataEncryptionKeyProperties tempDekProperties = new DataEncryptionKeyProperties(this.Id, encryptionAlgorithmId, keyWrapResponse.WrappedDataEncryptionKey, keyWrapResponse.EncryptionKeyWrapMetadata);
            InMemoryRawDek roundTripResponse = await this.UnwrapAsync(tempDekProperties, diagnosticsContext, cancellationToken);

            if (!roundTripResponse.RawDek.SequenceEqual(key))
            {
                throw CosmosExceptionFactory.CreateBadRequestException(ClientResources.KeyWrappingDidNotRoundtrip,
                                                                       diagnosticsContext: diagnosticsContext);
            }

            return(keyWrapResponse.WrappedDataEncryptionKey, keyWrapResponse.EncryptionKeyWrapMetadata, roundTripResponse);
        }
Beispiel #2
0
        private IEnumerable <byte> VerifyWrap(IEnumerable <byte> dek, EncryptionKeyWrapMetadata inputMetadata)
        {
            this.mockKeyWrapProvider.Verify(m => m.WrapKeyAsync(
                                                It.Is <byte[]>(key => key.SequenceEqual(dek)),
                                                inputMetadata,
                                                It.IsAny <CancellationToken>()),
                                            Times.Exactly(1));

            IEnumerable <byte> expectedWrappedKey = null;

            if (inputMetadata == this.metadata1)
            {
                expectedWrappedKey = dek.Select(b => (byte)(b + 1));
            }
            else if (inputMetadata == this.metadata2)
            {
                expectedWrappedKey = dek.Select(b => (byte)(b + 2));
            }
            else
            {
                Assert.Fail();
            }

            // Verify we did unwrap to check on the wrapping
            EncryptionKeyWrapMetadata expectedUpdatedMetadata = new EncryptionKeyWrapMetadata(inputMetadata.Value + this.metadataUpdateSuffix);

            this.VerifyUnwrap(expectedWrappedKey, expectedUpdatedMetadata);

            return(expectedWrappedKey);
        }
Beispiel #3
0
 private void VerifyUnwrap(IEnumerable <byte> wrappedDek, EncryptionKeyWrapMetadata inputMetadata)
 {
     this.mockKeyWrapProvider.Verify(m => m.UnwrapKeyAsync(
                                         It.Is <byte[]>(wrappedKey => wrappedKey.SequenceEqual(wrappedDek)),
                                         inputMetadata,
                                         It.IsAny <CancellationToken>()),
                                     Times.Exactly(1));
 }
 Task <DataEncryptionKeyResponse> CreateDataEncryptionKeyAsync(
     string id,
     CosmosEncryptionAlgorithm encryptionAlgorithm,
     EncryptionKeyWrapMetadata encryptionKeyWrapMetadata,
     RequestOptions requestOptions       = null,
     CancellationToken cancellationToken = default)
 {
     return(TaskHelper.RunInlineIfNeededAsync(() => this.database.CreateDataEncryptionKeyAsync(id, encryptionAlgorithm, encryptionKeyWrapMetadata, requestOptions, cancellationToken)));
 }
 /// <summary>
 /// Initializes a new instance of <see cref="DataEncryptionKeyProperties"/>.
 /// </summary>
 /// <param name="id">Unique identifier for the data encryption key.</param>
 /// <param name="encryptionAlgorithm">Encryption algorithm that will be used along with this data encryption key to encrypt/decrypt data.</param>
 /// <param name="wrappedDataEncryptionKey">Wrapped (encrypted) form of the data encryption key.</param>
 /// <param name="encryptionKeyWrapMetadata">Metadata used by the configured key wrapping provider in order to unwrap the key.</param>
 public DataEncryptionKeyProperties(
     string id,
     CosmosEncryptionAlgorithm encryptionAlgorithm,
     byte[] wrappedDataEncryptionKey,
     EncryptionKeyWrapMetadata encryptionKeyWrapMetadata)
 {
     this.Id = !string.IsNullOrEmpty(id) ? id : throw new ArgumentNullException(nameof(id));
     this.EncryptionAlgorithmId     = encryptionAlgorithm;
     this.WrappedDataEncryptionKey  = wrappedDataEncryptionKey ?? throw new ArgumentNullException(nameof(wrappedDataEncryptionKey));
     this.EncryptionKeyWrapMetadata = encryptionKeyWrapMetadata ?? throw new ArgumentNullException(nameof(encryptionKeyWrapMetadata));
 }
        async Task <DataEncryptionKeyResponse> CreateDataEncryptionKeyAsync(
            string id,
            CosmosEncryptionAlgorithm encryptionAlgorithmId,
            EncryptionKeyWrapMetadata encryptionKeyWrapMetadata,
            RequestOptions requestOptions       = null,
            CancellationToken cancellationToken = default)
        {
            if (string.IsNullOrEmpty(id))
            {
                throw new ArgumentNullException(nameof(id));
            }

            if (encryptionAlgorithmId != CosmosEncryptionAlgorithm.AE_AES_256_CBC_HMAC_SHA_256_RANDOMIZED)
            {
                throw new ArgumentException(string.Format("Unsupported Encryption Algorithm {0}", encryptionAlgorithmId), nameof(encryptionAlgorithmId));
            }

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

            this.ClientContext.ValidateResource(id);

            DataEncryptionKeyCore newDek = (DataEncryptionKeyInlineCore)this.GetDataEncryptionKey(id);

            CosmosDiagnosticsContext diagnosticsContext = CosmosDiagnosticsContext.Create(requestOptions);

            byte[] rawDek = newDek.GenerateKey(encryptionAlgorithmId);

            (byte[] wrappedDek, EncryptionKeyWrapMetadata updatedMetadata, InMemoryRawDek inMemoryRawDek) = await newDek.WrapAsync(
                rawDek,
                encryptionAlgorithmId,
                encryptionKeyWrapMetadata,
                diagnosticsContext,
                cancellationToken);

            DataEncryptionKeyProperties dekProperties = new DataEncryptionKeyProperties(id, encryptionAlgorithmId, wrappedDek, updatedMetadata);
            Stream streamPayload = this.ClientContext.SerializerCore.ToStream(dekProperties);
            Task <ResponseMessage> responseMessage = this.CreateDataEncryptionKeyStreamAsync(
                streamPayload,
                requestOptions,
                cancellationToken);

            DataEncryptionKeyResponse dekResponse = await this.ClientContext.ResponseFactory.CreateDataEncryptionKeyResponseAsync(newDek, responseMessage);

            Debug.Assert(dekResponse.Resource != null);

            this.ClientContext.DekCache.Set(this.Id, newDek.LinkUri, dekResponse.Resource);
            this.ClientContext.DekCache.SetRawDek(dekResponse.Resource.SelfLink, inMemoryRawDek);
            return(dekResponse);
        }
        /// <inheritdoc/>
        public override Task <DataEncryptionKeyResponse> RewrapAsync(
            EncryptionKeyWrapMetadata newWrapMetadata,
            RequestOptions requestOptions       = null,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            if (newWrapMetadata == null)
            {
                throw new ArgumentNullException(nameof(newWrapMetadata));
            }

            return(TaskHelper.RunInlineIfNeededAsync(() =>
                                                     this.dataEncryptionKey.RewrapAsync(newWrapMetadata, requestOptions, cancellationToken)));
        }
Beispiel #8
0
        /// <inheritdoc/>
        public override async Task <DataEncryptionKeyResponse> RewrapAsync(
            EncryptionKeyWrapMetadata newWrapMetadata,
            RequestOptions requestOptions       = null,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            if (newWrapMetadata == null)
            {
                throw new ArgumentNullException(nameof(newWrapMetadata));
            }

            CosmosDiagnosticsContext diagnosticsContext = CosmosDiagnosticsContext.Create(requestOptions);

            (DataEncryptionKeyProperties dekProperties, InMemoryRawDek inMemoryRawDek) = await this.FetchUnwrappedAsync(
                diagnosticsContext,
                cancellationToken);

            (byte[] wrappedDek, EncryptionKeyWrapMetadata updatedMetadata, InMemoryRawDek updatedRawDek) = await this.WrapAsync(
                inMemoryRawDek.RawDek,
                dekProperties.EncryptionAlgorithmId,
                newWrapMetadata,
                diagnosticsContext,
                cancellationToken);

            if (requestOptions == null)
            {
                requestOptions = new RequestOptions();
            }

            requestOptions.IfMatchEtag = dekProperties.ETag;

            DataEncryptionKeyProperties newDekProperties = new DataEncryptionKeyProperties(dekProperties);

            newDekProperties.WrappedDataEncryptionKey  = wrappedDek;
            newDekProperties.EncryptionKeyWrapMetadata = updatedMetadata;

            Task <ResponseMessage> responseMessage = this.ProcessStreamAsync(
                this.ClientContext.SerializerCore.ToStream(newDekProperties),
                OperationType.Replace,
                requestOptions,
                diagnosticsContext,
                cancellationToken);

            DataEncryptionKeyResponse response = await this.ClientContext.ResponseFactory.CreateDataEncryptionKeyResponseAsync(this, responseMessage);

            Debug.Assert(response.Resource != null);

            this.ClientContext.DekCache.Set(this.Database.Id, this.LinkUri, response.Resource);
            this.ClientContext.DekCache.SetRawDek(response.Resource.ResourceId, updatedRawDek);
            return(response);
        }
Beispiel #9
0
        private Container GetContainerWithMockSetup(EncryptionTestHandler encryptionTestHandler = null)
        {
            this.testHandler = encryptionTestHandler ?? new EncryptionTestHandler();

            this.mockKeyWrapProvider = new Mock <EncryptionKeyWrapProvider>();
            this.mockKeyWrapProvider.Setup(m => m.WrapKeyAsync(It.IsAny <byte[]>(), It.IsAny <EncryptionKeyWrapMetadata>(), It.IsAny <CancellationToken>()))
            .ReturnsAsync((byte[] key, EncryptionKeyWrapMetadata metadata, CancellationToken cancellationToken) =>
            {
                EncryptionKeyWrapMetadata responseMetadata = new EncryptionKeyWrapMetadata(metadata.Value + this.metadataUpdateSuffix);
                int moveBy = metadata.Value == this.metadata1.Value ? 1 : 2;
                return(new EncryptionKeyWrapResult(key.Select(b => (byte)(b + moveBy)).ToArray(), responseMetadata));
            });
            this.mockKeyWrapProvider.Setup(m => m.UnwrapKeyAsync(It.IsAny <byte[]>(), It.IsAny <EncryptionKeyWrapMetadata>(), It.IsAny <CancellationToken>()))
            .ReturnsAsync((byte[] wrappedKey, EncryptionKeyWrapMetadata metadata, CancellationToken cancellationToken) =>
            {
                int moveBy = metadata.Value == this.metadata1.Value + this.metadataUpdateSuffix ? 1 : 2;
                return(new EncryptionKeyUnwrapResult(wrappedKey.Select(b => (byte)(b - moveBy)).ToArray(), this.cacheTTL));
            });

            CosmosClient client = MockCosmosUtil.CreateMockCosmosClient((builder) => builder
                                                                        .AddCustomHandlers(this.testHandler)
                                                                        .WithEncryptionKeyWrapProvider(this.mockKeyWrapProvider.Object));

            this.mockEncryptionAlgorithm = new Mock <EncryptionAlgorithm>();
            this.mockEncryptionAlgorithm.Setup(m => m.EncryptData(It.IsAny <byte[]>()))
            .Returns((byte[] plainText) => plainText.Reverse().ToArray());
            this.mockEncryptionAlgorithm.Setup(m => m.DecryptData(It.IsAny <byte[]>()))
            .Returns((byte[] cipherText) => cipherText.Reverse().ToArray());
            this.mockEncryptionAlgorithm.SetupGet(m => m.AlgorithmName).Returns(AeadAes256CbcHmac256Algorithm.AlgorithmNameConstant);

            this.mockDatabaseCore          = new Mock <DatabaseCore>(client.ClientContext, EncryptionUnitTests.DatabaseId);
            this.mockDatabaseCore.CallBase = true;
            this.mockDatabaseCore.Setup(m => m.GetDataEncryptionKey(It.IsAny <string>()))
            .Returns((string id) =>
            {
                Mock <DataEncryptionKeyCore> mockDekCore = new Mock <DataEncryptionKeyCore>(client.ClientContext, this.mockDatabaseCore.Object, id);
                mockDekCore.CallBase = true;
                mockDekCore.Setup(m => m.GenerateKey(EncryptionUnitTests.Algo)).Returns(this.dek);
                mockDekCore.Setup(m => m.GetEncryptionAlgorithm(It.IsAny <byte[]>(), EncryptionUnitTests.Algo))
                .Returns(this.mockEncryptionAlgorithm.Object);
                return(new DataEncryptionKeyInlineCore(mockDekCore.Object));
            });

            return(new ContainerInlineCore(new ContainerCore(client.ClientContext, this.mockDatabaseCore.Object, EncryptionUnitTests.ContainerId)));
        }
 /// <summary>
 /// Wraps the raw data encryption key (after unwrapping using the old metadata if needed) using the provided
 /// metadata with the help of the key wrapping provider in the EncryptionSerializer configured on the client via
 /// <see cref="CosmosClientBuilder.WithCustomSerializer"/>, and saves the re-wrapped data encryption key as an asynchronous
 /// operation in the Azure Cosmos service.
 /// </summary>
 /// <param name="newWrapMetadata">The metadata using which the data encryption key needs to now be wrapped.</param>
 /// <param name="requestOptions">(Optional) The options for the request.</param>
 /// <param name="cancellationToken">(Optional) Token representing request cancellation.</param>
 /// <returns>An awaitable response which wraps a <see cref="DataEncryptionKeyProperties"/> containing details of the data encryption key that was re-wrapped.</returns>
 /// <exception cref="CosmosException">
 /// This exception can encapsulate many different types of errors.
 /// To determine the specific error always look at the StatusCode property.
 /// Some common codes you may get when re-wrapping a data encryption key are:
 /// <list type="table">
 ///     <listheader>
 ///         <term>StatusCode</term>
 ///         <description>Reason for exception</description>
 ///     </listheader>
 ///     <item>
 ///         <term>404</term>
 ///         <description>
 ///         NotFound - This means the resource or parent resource you tried to replace did not exist.
 ///         </description>
 ///     </item>
 ///     <item>
 ///         <term>429</term>
 ///         <description>
 ///         TooManyRequests - This means you have exceeded the number of request units per second.
 ///         Consult the CosmosException.RetryAfter value to see how long you should wait before retrying this operation.
 ///         </description>
 ///     </item>
 /// </list>
 /// </exception>
 /// <example>
 /// <code language="c#">
 /// <![CDATA[
 /// AzureKeyVaultKeyWrapMetadata v2Metadata = new AzureKeyVaultKeyWrapMetadata("/path/to/my/master/key/v2");
 /// await key.RewrapAsync(v2Metadata);
 /// ]]>
 /// </code>
 /// </example>
 public abstract Task <DataEncryptionKeyResponse> RewrapAsync(
     EncryptionKeyWrapMetadata newWrapMetadata,
     RequestOptions requestOptions       = null,
     CancellationToken cancellationToken = default(CancellationToken));
Beispiel #11
0
 /// <summary>
 /// Initializes a new instance of the result of wrapping a data encryption key.
 /// </summary>
 /// <param name="wrappedDataEncryptionKey">
 /// Wrapped form of data encryption key.
 /// The byte array passed in must not be modified after this call by the <see cref="EncryptionKeyWrapProvider"/>.
 /// </param>
 /// <param name="encryptionKeyWrapMetadata">Metadata that can be used by the wrap provider to unwrap the data encryption key.</param>
 public EncryptionKeyWrapResult(byte[] wrappedDataEncryptionKey, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata)
 {
     this.WrappedDataEncryptionKey  = wrappedDataEncryptionKey ?? throw new ArgumentNullException(nameof(wrappedDataEncryptionKey));
     this.EncryptionKeyWrapMetadata = encryptionKeyWrapMetadata ?? throw new ArgumentNullException(nameof(encryptionKeyWrapMetadata));
 }
Beispiel #12
0
 /// <summary>
 /// Unwraps (i.e. decrypts) the provided wrapped data encryption key.
 /// </summary>
 /// <param name="wrappedKey">Wrapped form of data encryption key that needs to be unwrapped.</param>
 /// <param name="metadata">Metadata for the wrap provider that should be used to wrap / unwrap the key.</param>
 /// <param name="cancellationToken">Cancellation token allowing for cancellation of this operation.</param>
 /// <returns>Awaitable unwrapped (i.e. unencrypted) version of data encryption key passed in and how long the raw data encryption key can be cached on the client.</returns>
 public abstract Task <EncryptionKeyUnwrapResult> UnwrapKeyAsync(byte[] wrappedKey, EncryptionKeyWrapMetadata metadata, CancellationToken cancellationToken);