internal async Task <(byte[], EncryptionKeyWrapMetadata, InMemoryRawDek)> WrapAsync( string id, byte[] key, string encryptionAlgorithm, EncryptionKeyWrapMetadata metadata, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { EncryptionKeyWrapResult keyWrapResponse = null; using (diagnosticsContext.CreateScope("WrapDataEncryptionKey")) { keyWrapResponse = string.Equals(encryptionAlgorithm, CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized) && this.DekProvider.EncryptionKeyWrapProvider != null ? await this.DekProvider.EncryptionKeyWrapProvider.WrapKeyAsync(key, metadata, cancellationToken) : string.Equals(encryptionAlgorithm, CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized) && this.DekProvider.MdeKeyWrapProvider != null ? await this.DekProvider.MdeKeyWrapProvider.WrapKeyAsync(key, metadata, cancellationToken) : throw new ArgumentException(string.Format( "Unsupported encryption algorithm {0}." + " Please initialize the Encryptor or CosmosDataEncryptionKeyProvider with an implementation of the EncryptionKeyStoreProvider / EncryptionKeyWrapProvider.", encryptionAlgorithm)); } // Verify DataEncryptionKeyProperties tempDekProperties = new DataEncryptionKeyProperties( id, encryptionAlgorithm, keyWrapResponse.WrappedDataEncryptionKey, keyWrapResponse.EncryptionKeyWrapMetadata, DateTime.UtcNow); byte[] rawKey = null; InMemoryRawDek roundTripResponse = null; if (string.Equals(encryptionAlgorithm, CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized)) { roundTripResponse = await this.UnwrapAsync(tempDekProperties, diagnosticsContext, cancellationToken); rawKey = roundTripResponse.DataEncryptionKey.RawKey; } else if (string.Equals(encryptionAlgorithm, CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized)) { EncryptionKeyUnwrapResult unwrapResult = await this.UnWrapDekMdeEncAlgoAsync( tempDekProperties, diagnosticsContext, cancellationToken); rawKey = unwrapResult.DataEncryptionKey; } if (!rawKey.SequenceEqual(key)) { throw new InvalidOperationException("The key wrapping provider configured was unable to unwrap the wrapped key correctly."); } return(keyWrapResponse.WrappedDataEncryptionKey, keyWrapResponse.EncryptionKeyWrapMetadata, roundTripResponse); }
/// <inheritdoc/> public override async Task <ItemResponse <DataEncryptionKeyProperties> > RewrapDataEncryptionKeyAsync( string id, EncryptionKeyWrapMetadata newWrapMetadata, ItemRequestOptions requestOptions = null, CancellationToken cancellationToken = default) { if (newWrapMetadata == null) { throw new ArgumentNullException(nameof(newWrapMetadata)); } CosmosDiagnosticsContext diagnosticsContext = CosmosDiagnosticsContext.Create(requestOptions); DataEncryptionKeyProperties dekProperties = await this.FetchDataEncryptionKeyPropertiesAsync( id, diagnosticsContext, cancellationToken); byte[] rawkey = null; if (string.Equals(dekProperties.EncryptionAlgorithm, CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized)) { InMemoryRawDek inMemoryRawDek = await this.FetchUnwrappedAsync( dekProperties, diagnosticsContext, cancellationToken); rawkey = inMemoryRawDek.DataEncryptionKey.RawKey; } else if (string.Equals(dekProperties.EncryptionAlgorithm, CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized)) { EncryptionKeyUnwrapResult encryptionKeyUnwrapResult = await this.DekProvider.MdeKeyWrapProvider.UnwrapKeyAsync( dekProperties.WrappedDataEncryptionKey, dekProperties.EncryptionKeyWrapMetadata, cancellationToken); rawkey = encryptionKeyUnwrapResult.DataEncryptionKey; } (byte[] wrappedDek, EncryptionKeyWrapMetadata updatedMetadata, InMemoryRawDek updatedRawDek) = await this.WrapAsync( id, rawkey, dekProperties.EncryptionAlgorithm, newWrapMetadata, diagnosticsContext, cancellationToken); if (requestOptions == null) { requestOptions = new ItemRequestOptions(); } requestOptions.IfMatchEtag = dekProperties.ETag; DataEncryptionKeyProperties newDekProperties = new DataEncryptionKeyProperties(dekProperties) { WrappedDataEncryptionKey = wrappedDek, EncryptionKeyWrapMetadata = updatedMetadata, }; ItemResponse <DataEncryptionKeyProperties> response; try { response = await this.DekProvider.Container.ReplaceItemAsync( newDekProperties, newDekProperties.Id, new PartitionKey(newDekProperties.Id), requestOptions, cancellationToken); Debug.Assert(response.Resource != null); } catch (CosmosException ex) { if (!ex.StatusCode.Equals(HttpStatusCode.PreconditionFailed)) { throw; } // Handle if exception is due to etag mismatch. The scenario is as follows - say there are 2 clients A and B that both have the DEK properties cached. // From A, rewrap worked and the DEK is updated. Now from B, rewrap was attempted later based on the cached properties which will fail due to etag mismatch. // To address this, we do an explicit read, which reads the key from storage and updates the cached properties; and then attempt rewrap again. await this.ReadDataEncryptionKeyAsync( newDekProperties.Id, requestOptions, cancellationToken); return(await this.RewrapDataEncryptionKeyAsync( id, newWrapMetadata, requestOptions, cancellationToken)); } this.DekProvider.DekCache.SetDekProperties(id, response.Resource); if (string.Equals(dekProperties.EncryptionAlgorithm, CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized)) { this.DekProvider.DekCache.SetRawDek(id, updatedRawDek); } return(response); }