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); }
internal async Task <InMemoryRawDek> UnwrapAsync( DataEncryptionKeyProperties dekProperties, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { EncryptionKeyWrapProvider encryptionKeyWrapProvider = this.ClientContext.ClientOptions.EncryptionKeyWrapProvider; if (encryptionKeyWrapProvider == null) { throw new ArgumentException(ClientResources.EncryptionKeyWrapProviderNotConfigured); } EncryptionKeyUnwrapResult unwrapResult = null; using (diagnosticsContext.CreateScope("UnwrapDataEncryptionKey")) { unwrapResult = await encryptionKeyWrapProvider.UnwrapKeyAsync( dekProperties.WrappedDataEncryptionKey, dekProperties.EncryptionKeyWrapMetadata, cancellationToken); } EncryptionAlgorithm encryptionAlgorithm = this.GetEncryptionAlgorithm(unwrapResult.DataEncryptionKey, dekProperties.EncryptionAlgorithmId); return(new InMemoryRawDek(unwrapResult.DataEncryptionKey, encryptionAlgorithm, unwrapResult.ClientCacheTimeToLive)); }
internal async Task <(DataEncryptionKeyProperties, InMemoryRawDek)> FetchUnwrappedAsync( CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { DataEncryptionKeyProperties dekProperties = null; try { dekProperties = await this.ClientContext.DekCache.GetOrAddByNameLinkUriAsync( this.LinkUri, this.Database.Id, this.ReadResourceAsync, diagnosticsContext, cancellationToken); } catch (CosmosException ex) when(ex.StatusCode == HttpStatusCode.NotFound) { throw CosmosExceptionFactory.CreateNotFoundException( ClientResources.DataEncryptionKeyNotFound, diagnosticsContext: diagnosticsContext, innerException: ex); } InMemoryRawDek inMemoryRawDek = await this.ClientContext.DekCache.GetOrAddRawDekAsync( dekProperties, this.UnwrapAsync, diagnosticsContext, cancellationToken); return(dekProperties, inMemoryRawDek); }
public void Set(string databaseId, Uri dekNameLinkUri, DataEncryptionKeyProperties dekProperties) { CachedDekProperties cachedDekProperties = new CachedDekProperties(databaseId, dekProperties, DateTime.UtcNow + this.dekPropertiesTimeToLive); this.DekPropertiesByNameLinkUriCache.Set(dekNameLinkUri, cachedDekProperties); this.DekPropertiesByRidSelfLinkCache.Set(dekProperties.SelfLink, cachedDekProperties); }
private async Task <CachedDekProperties> FetchAsync( Func <CosmosDiagnosticsContext, CancellationToken, Task <DataEncryptionKeyProperties> > fetcher, string databaseId, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { DataEncryptionKeyProperties serverProperties = await fetcher(diagnosticsContext, cancellationToken); return(new CachedDekProperties(databaseId, serverProperties, DateTime.UtcNow + this.dekPropertiesTimeToLive)); }
public CachedDekProperties( string databaseId, DataEncryptionKeyProperties serverProperties, DateTime serverPropertiesExpiryUtc) { Debug.Assert(databaseId != null); Debug.Assert(serverProperties != null); this.DatabaseId = databaseId; this.ServerProperties = serverProperties; this.ServerPropertiesExpiryUtc = serverPropertiesExpiryUtc; }
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 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); }
internal Task <DataEncryptionKeyResponse> CreateDataEncryptionKeyResponseAsync( DataEncryptionKey dataEncryptionKey, Task <ResponseMessage> cosmosResponseMessageTask) { return(this.ProcessMessageAsync(cosmosResponseMessageTask, (cosmosResponseMessage) => { DataEncryptionKeyProperties dekProperties = this.ToObjectInternal <DataEncryptionKeyProperties>(cosmosResponseMessage); return new DataEncryptionKeyResponse( cosmosResponseMessage.StatusCode, cosmosResponseMessage.Headers, dekProperties, dataEncryptionKey, cosmosResponseMessage.Diagnostics); })); }
public async Task EncryptionUTRewrapDek() { Container container = this.GetContainerWithMockSetup(); DatabaseCore database = (DatabaseCore)((ContainerCore)(ContainerInlineCore)container).Database; string dekId = "mydek"; DataEncryptionKeyResponse createResponse = await database.CreateDataEncryptionKeyAsync(dekId, EncryptionUnitTests.Algo, this.metadata1); DataEncryptionKeyProperties createdProperties = createResponse.Resource; Assert.AreEqual(HttpStatusCode.Created, createResponse.StatusCode); this.VerifyWrap(this.dek, this.metadata1); DataEncryptionKey dek = database.GetDataEncryptionKey(dekId); DataEncryptionKeyResponse rewrapResponse = await dek.RewrapAsync(this.metadata2); DataEncryptionKeyProperties rewrappedProperties = rewrapResponse.Resource; Assert.IsNotNull(rewrappedProperties); Assert.AreEqual(dekId, rewrappedProperties.Id); Assert.AreEqual(createdProperties.CreatedTime, rewrappedProperties.CreatedTime); Assert.IsNotNull(rewrappedProperties.LastModified); Assert.AreEqual(createdProperties.ResourceId, rewrappedProperties.ResourceId); Assert.AreEqual(createdProperties.SelfLink, rewrappedProperties.SelfLink); IEnumerable <byte> expectedRewrappedKey = this.dek.Select(b => (byte)(b + 2)); Assert.IsTrue(expectedRewrappedKey.SequenceEqual(rewrappedProperties.WrappedDataEncryptionKey)); Assert.AreEqual(new EncryptionKeyWrapMetadata(this.metadata2.Value + this.metadataUpdateSuffix), rewrappedProperties.EncryptionKeyWrapMetadata); Assert.AreEqual(2, this.testHandler.Received.Count); RequestMessage rewrapRequestMessage = this.testHandler.Received[1]; Assert.AreEqual(ResourceType.ClientEncryptionKey, rewrapRequestMessage.ResourceType); Assert.AreEqual(OperationType.Replace, rewrapRequestMessage.OperationType); Assert.AreEqual(createResponse.ETag, rewrapRequestMessage.Headers[HttpConstants.HttpHeaders.IfMatch]); Assert.IsTrue(this.testHandler.Deks.ContainsKey(dekId)); DataEncryptionKeyProperties serverDekProperties = this.testHandler.Deks[dekId]; Assert.IsTrue(serverDekProperties.Equals(rewrappedProperties)); this.VerifyWrap(this.dek, this.metadata2); this.mockKeyWrapProvider.VerifyNoOtherCalls(); }
internal async Task <(DataEncryptionKeyProperties, InMemoryRawDek)> FetchUnwrappedByRidAsync( string rid, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { string dekRidSelfLink = PathsHelper.GeneratePath(ResourceType.ClientEncryptionKey, rid, isFeed: false); // Server self links end with / but client generate links don't - match them. if (!dekRidSelfLink.EndsWith("/")) { dekRidSelfLink += "/"; } DataEncryptionKeyProperties dekProperties = null; try { dekProperties = await this.ClientContext.DekCache.GetOrAddByRidSelfLinkAsync( dekRidSelfLink, this.Database.Id, this.ReadResourceByRidSelfLinkAsync, this.LinkUri, diagnosticsContext, cancellationToken); } catch (CosmosException ex) when(ex.StatusCode == HttpStatusCode.NotFound) { throw CosmosExceptionFactory.CreateNotFoundException( ClientResources.DataEncryptionKeyNotFound, diagnosticsContext: diagnosticsContext, innerException: ex); } InMemoryRawDek inMemoryRawDek = await this.ClientContext.DekCache.GetOrAddRawDekAsync( dekProperties, this.UnwrapAsync, diagnosticsContext, cancellationToken); return(dekProperties, inMemoryRawDek); }
public async Task <InMemoryRawDek> GetOrAddRawDekAsync( DataEncryptionKeyProperties dekProperties, Func <DataEncryptionKeyProperties, CosmosDiagnosticsContext, CancellationToken, Task <InMemoryRawDek> > unwrapper, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { InMemoryRawDek inMemoryRawDek = await this.RawDekByRidSelfLinkCache.GetAsync( dekProperties.SelfLink, null, () => unwrapper(dekProperties, diagnosticsContext, cancellationToken), cancellationToken); if (inMemoryRawDek.RawDekExpiry <= DateTime.UtcNow) { inMemoryRawDek = await this.RawDekByRidSelfLinkCache.GetAsync( dekProperties.SelfLink, null, () => unwrapper(dekProperties, diagnosticsContext, cancellationToken), cancellationToken, forceRefresh : true); } return(inMemoryRawDek); }
public async Task EncryptionUTCreateDek() { Container container = this.GetContainerWithMockSetup(); DatabaseCore database = (DatabaseCore)((ContainerCore)(ContainerInlineCore)container).Database; string dekId = "mydek"; DataEncryptionKeyResponse dekResponse = await database.CreateDataEncryptionKeyAsync(dekId, EncryptionUnitTests.Algo, this.metadata1); Assert.AreEqual(HttpStatusCode.Created, dekResponse.StatusCode); Assert.AreEqual(requestCharge, dekResponse.RequestCharge); Assert.IsNotNull(dekResponse.ETag); DataEncryptionKeyProperties dekProperties = dekResponse.Resource; Assert.IsNotNull(dekProperties); Assert.AreEqual(dekResponse.ETag, dekProperties.ETag); Assert.AreEqual(dekId, dekProperties.Id); Assert.AreEqual(1, this.testHandler.Received.Count); RequestMessage createDekRequestMessage = this.testHandler.Received[0]; Assert.AreEqual(ResourceType.ClientEncryptionKey, createDekRequestMessage.ResourceType); Assert.AreEqual(OperationType.Create, createDekRequestMessage.OperationType); Assert.IsTrue(this.testHandler.Deks.ContainsKey(dekId)); DataEncryptionKeyProperties serverDekProperties = this.testHandler.Deks[dekId]; Assert.IsTrue(serverDekProperties.Equals(dekProperties)); // Make sure we didn't push anything else in the JSON (such as raw DEK) by comparing JSON properties // to properties exposed in DataEncryptionKeyProperties. createDekRequestMessage.Content.Position = 0; // it is a test assumption that the client uses MemoryStream JObject jObj = JObject.Parse(await new StreamReader(createDekRequestMessage.Content).ReadToEndAsync()); IEnumerable <string> dekPropertiesPropertyNames = GetJsonPropertyNamesForType(typeof(DataEncryptionKeyProperties)); foreach (JProperty property in jObj.Properties()) { Assert.IsTrue(dekPropertiesPropertyNames.Contains(property.Name)); } // Key wrap metadata should be the only "object" child in the JSON (given current properties in DataEncryptionKeyProperties) IEnumerable <JToken> objectChildren = jObj.PropertyValues().Where(v => v.Type == JTokenType.Object); Assert.AreEqual(1, objectChildren.Count()); JObject keyWrapMetadataJObj = (JObject)objectChildren.First(); Assert.AreEqual(Constants.Properties.KeyWrapMetadata, ((JProperty)keyWrapMetadataJObj.Parent).Name); IEnumerable <string> keyWrapMetadataPropertyNames = GetJsonPropertyNamesForType(typeof(EncryptionKeyWrapMetadata)); foreach (JProperty property in keyWrapMetadataJObj.Properties()) { Assert.IsTrue(keyWrapMetadataPropertyNames.Contains(property.Name)); } IEnumerable <byte> expectedWrappedKey = this.VerifyWrap(this.dek, this.metadata1); this.mockKeyWrapProvider.VerifyNoOtherCalls(); Assert.IsTrue(expectedWrappedKey.SequenceEqual(dekProperties.WrappedDataEncryptionKey)); }
public override async Task <ResponseMessage> SendAsync( RequestMessage request, CancellationToken cancellationToken) { // We clone the request message as the Content is disposed before we can use it in the test assertions later. this.Received.Add(EncryptionTestHandler.CloneRequestMessage(request)); if (this.func != null) { return(await this.func(request)); } HttpStatusCode httpStatusCode = HttpStatusCode.InternalServerError; if (request.ResourceType == ResourceType.ClientEncryptionKey) { DataEncryptionKeyProperties dekProperties = null; if (request.OperationType == OperationType.Create) { dekProperties = this.serializer.FromStream <DataEncryptionKeyProperties>(request.Content); string databaseRid = ResourceId.NewDatabaseId(1).ToString(); dekProperties.ResourceId = ResourceId.NewClientEncryptionKeyId(databaseRid, (uint)this.Received.Count).ToString(); dekProperties.CreatedTime = EncryptionTestHandler.ReducePrecisionToSeconds(DateTime.UtcNow); dekProperties.LastModified = dekProperties.CreatedTime; dekProperties.ETag = Guid.NewGuid().ToString(); dekProperties.SelfLink = string.Format( "dbs/{0}/{1}/{2}/", databaseRid, Paths.ClientEncryptionKeysPathSegment, dekProperties.ResourceId); httpStatusCode = HttpStatusCode.Created; if (!this.Deks.TryAdd(dekProperties.Id, dekProperties)) { httpStatusCode = HttpStatusCode.Conflict; } } else if (request.OperationType == OperationType.Read) { string dekId = EncryptionTestHandler.ParseDekUri(request.RequestUri); httpStatusCode = HttpStatusCode.OK; if (!this.Deks.TryGetValue(dekId, out dekProperties)) { httpStatusCode = HttpStatusCode.NotFound; } } else if (request.OperationType == OperationType.Replace) { string dekId = EncryptionTestHandler.ParseDekUri(request.RequestUri); dekProperties = this.serializer.FromStream <DataEncryptionKeyProperties>(request.Content); dekProperties.LastModified = EncryptionTestHandler.ReducePrecisionToSeconds(DateTime.UtcNow); dekProperties.ETag = Guid.NewGuid().ToString(); httpStatusCode = HttpStatusCode.OK; if (!this.Deks.TryGetValue(dekId, out DataEncryptionKeyProperties existingDekProperties)) { httpStatusCode = HttpStatusCode.NotFound; } if (!this.Deks.TryUpdate(dekId, dekProperties, existingDekProperties)) { throw new InvalidOperationException("Concurrency not handled in tests."); } } else if (request.OperationType == OperationType.Delete) { string dekId = EncryptionTestHandler.ParseDekUri(request.RequestUri); httpStatusCode = HttpStatusCode.NoContent; if (!this.Deks.TryRemove(dekId, out _)) { httpStatusCode = HttpStatusCode.NotFound; } } ResponseMessage responseMessage = new ResponseMessage(httpStatusCode, request) { Content = dekProperties != null?this.serializer.ToStream(dekProperties) : null, }; responseMessage.Headers.RequestCharge = EncryptionUnitTests.requestCharge; responseMessage.Headers.ETag = dekProperties?.ETag; return(responseMessage); } else if (request.ResourceType == ResourceType.Document) { JObject item = null; if (request.OperationType == OperationType.Create) { item = EncryptionUnitTests.ParseStream(request.Content); string itemId = item.Property("id").Value.Value <string>(); httpStatusCode = HttpStatusCode.Created; if (!this.Items.TryAdd(itemId, item)) { httpStatusCode = HttpStatusCode.Conflict; } } else if (request.OperationType == OperationType.Read) { string itemId = EncryptionTestHandler.ParseItemUri(request.RequestUri); httpStatusCode = HttpStatusCode.OK; if (!this.Items.TryGetValue(itemId, out item)) { httpStatusCode = HttpStatusCode.NotFound; } } else if (request.OperationType == OperationType.Replace) { string itemId = EncryptionTestHandler.ParseItemUri(request.RequestUri); item = EncryptionUnitTests.ParseStream(request.Content); httpStatusCode = HttpStatusCode.OK; if (!this.Items.TryGetValue(itemId, out JObject existingItem)) { httpStatusCode = HttpStatusCode.NotFound; } if (!this.Items.TryUpdate(itemId, item, existingItem)) { throw new InvalidOperationException("Concurrency not handled in tests."); } } else if (request.OperationType == OperationType.Delete) { string itemId = EncryptionTestHandler.ParseItemUri(request.RequestUri); httpStatusCode = HttpStatusCode.NoContent; if (!this.Items.TryRemove(itemId, out _)) { httpStatusCode = HttpStatusCode.NotFound; } } ResponseMessage responseMessage = new ResponseMessage(httpStatusCode, request) { Content = item != null?this.serializer.ToStream(item) : null, }; responseMessage.Headers.RequestCharge = EncryptionUnitTests.requestCharge; return(responseMessage); } return(new ResponseMessage(httpStatusCode, request)); }