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); }
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); }
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))); }
/// <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); }
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));
/// <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)); }
/// <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);