internal async Task <InMemoryRawDek> FetchUnwrappedAsync( DataEncryptionKeyProperties dekProperties, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { try { if (string.Equals(dekProperties.EncryptionAlgorithm, CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized)) { DataEncryptionKey dek = this.InitMdeEncryptionAlgorithm(dekProperties); // TTL is not used since DEK is not cached. return(new InMemoryRawDek(dek, TimeSpan.FromMilliseconds(0))); } return(await this.DekProvider.DekCache.GetOrAddRawDekAsync( dekProperties, this.UnwrapAsync, diagnosticsContext, cancellationToken)); } catch (Exception exception) { throw EncryptionExceptionFactory.EncryptionKeyNotFoundException( $"Failed to unwrap Data Encryption Key with id: '{dekProperties.Id}'.", exception); } }
internal async Task <InMemoryRawDek> UnwrapAsync( DataEncryptionKeyProperties dekProperties, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { EncryptionKeyUnwrapResult unwrapResult; if (this.DekProvider.EncryptionKeyWrapProvider == null) { throw new InvalidOperationException($"For use of '{CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized}' algorithm, " + "Encryptor or CosmosDataEncryptionKeyProvider needs to be initialized with EncryptionKeyWrapProvider."); } using (diagnosticsContext.CreateScope("UnwrapDataEncryptionKey")) { unwrapResult = await this.DekProvider.EncryptionKeyWrapProvider.UnwrapKeyAsync( dekProperties.WrappedDataEncryptionKey, dekProperties.EncryptionKeyWrapMetadata, cancellationToken); } DataEncryptionKey dek = DataEncryptionKey.Create( unwrapResult.DataEncryptionKey, dekProperties.EncryptionAlgorithm); return(new InMemoryRawDek(dek, unwrapResult.ClientCacheTimeToLive)); }
internal async Task <DataEncryptionKey> FetchUnWrappedLegacySupportedMdeDekAsync( DataEncryptionKeyProperties dekProperties, string encryptionAlgorithm, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { EncryptionKeyUnwrapResult unwrapResult; if (this.DekProvider.EncryptionKeyStoreProvider == null) { throw new InvalidOperationException($"For use of '{CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized}' algorithm based DEK, " + "Encryptor or CosmosDataEncryptionKeyProvider needs to be initialized with EncryptionKeyStoreProvider."); } try { using (diagnosticsContext.CreateScope("UnwrapDataEncryptionKey")) { unwrapResult = await this.UnWrapDekMdeEncAlgoAsync( dekProperties, diagnosticsContext, cancellationToken); } return(DataEncryptionKey.Create( unwrapResult.DataEncryptionKey, encryptionAlgorithm)); } catch (Exception exception) { throw EncryptionExceptionFactory.EncryptionKeyNotFoundException( $"Failed to unwrap Data Encryption Key with id: '{dekProperties.Id}'.", exception); } }
public CachedDekProperties( DataEncryptionKeyProperties serverProperties, DateTime serverPropertiesExpiryUtc) { Debug.Assert(serverProperties != null); this.ServerProperties = serverProperties; this.ServerPropertiesExpiryUtc = serverPropertiesExpiryUtc; }
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); }
private async Task <CachedDekProperties> FetchAsync( string dekId, Func <string, CosmosDiagnosticsContext, CancellationToken, Task <DataEncryptionKeyProperties> > fetcher, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { DataEncryptionKeyProperties serverProperties = await fetcher(dekId, diagnosticsContext, cancellationToken); return(new CachedDekProperties(serverProperties, DateTime.UtcNow + this.dekPropertiesTimeToLive)); }
/// <summary> /// Initializes a new instance of MdeEncryptionAlgorithm. /// Uses <see cref="AeadAes256CbcHmac256EncryptionAlgorithm"/> which implements authenticated encryption algorithm with associated data as described /// <see href="http://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05">here</see> . /// More specifically this implements AEAD_AES_256_CBC_HMAC_SHA256 algorithm. /// </summary> /// <param name="dekProperties"> Data Encryption Key properties</param> /// <param name="encryptionType"> Encryption type </param> /// <param name="encryptionKeyStoreProvider"> EncryptionKeyStoreProvider for wrapping and unwrapping </param> public MdeEncryptionAlgorithm( DataEncryptionKeyProperties dekProperties, Data.Encryption.Cryptography.EncryptionType encryptionType, EncryptionKeyStoreProvider encryptionKeyStoreProvider, TimeSpan?cacheTimeToLive) { if (dekProperties == null) { throw new ArgumentNullException(nameof(dekProperties)); } if (encryptionKeyStoreProvider == null) { throw new ArgumentNullException(nameof(encryptionKeyStoreProvider)); } KeyEncryptionKey keyEncryptionKey = KeyEncryptionKey.GetOrCreate( dekProperties.EncryptionKeyWrapMetadata.Name, dekProperties.EncryptionKeyWrapMetadata.Value, encryptionKeyStoreProvider); ProtectedDataEncryptionKey protectedDataEncryptionKey; if (cacheTimeToLive.HasValue) { // no caching if (cacheTimeToLive.Value == TimeSpan.Zero) { protectedDataEncryptionKey = new ProtectedDataEncryptionKey( dekProperties.Id, keyEncryptionKey, dekProperties.WrappedDataEncryptionKey); } else { protectedDataEncryptionKey = ProtectedDataEncryptionKey.GetOrCreate( dekProperties.Id, keyEncryptionKey, dekProperties.WrappedDataEncryptionKey); protectedDataEncryptionKey.TimeToLive = cacheTimeToLive.Value; } } else { protectedDataEncryptionKey = ProtectedDataEncryptionKey.GetOrCreate( dekProperties.Id, keyEncryptionKey, dekProperties.WrappedDataEncryptionKey); } this.mdeAeadAes256CbcHmac256EncryptionAlgorithm = AeadAes256CbcHmac256EncryptionAlgorithm.GetOrCreate( protectedDataEncryptionKey, encryptionType); }
internal DataEncryptionKey InitMdeEncryptionAlgorithm(DataEncryptionKeyProperties dekProperties) { if (this.DekProvider.MdeKeyWrapProvider == null) { throw new InvalidOperationException($"For use of '{CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized}' algorithm, " + "Encryptor or CosmosDataEncryptionKeyProvider needs to be initialized with EncryptionKeyStoreProvider."); } return(new MdeEncryptionAlgorithm( dekProperties, Data.Encryption.Cryptography.EncryptionType.Randomized, this.DekProvider.MdeKeyWrapProvider.EncryptionKeyStoreProvider, this.DekProvider.PdekCacheTimeToLive)); }
internal async Task <DataEncryptionKeyProperties> FetchDataEncryptionKeyPropertiesAsync( string id, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { try { DataEncryptionKeyProperties dekProperties = await this.DekProvider.DekCache.GetOrAddDekPropertiesAsync( id, this.ReadResourceAsync, diagnosticsContext, cancellationToken); return(dekProperties); } catch (CosmosException exception) { throw EncryptionExceptionFactory.EncryptionKeyNotFoundException( $"Failed to retrieve Data Encryption Key with id: '{id}'.", exception); } }
private async Task <EncryptionKeyUnwrapResult> UnWrapDekMdeEncAlgoAsync( DataEncryptionKeyProperties dekProperties, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { EncryptionKeyUnwrapResult unwrapResult; if (this.DekProvider.MdeKeyWrapProvider == null) { throw new InvalidOperationException($"For use of '{CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized}' algorithm, " + "Encryptor or CosmosDataEncryptionKeyProvider needs to be initialized with EncryptionKeyStoreProvider."); } using (diagnosticsContext.CreateScope("UnwrapDataEncryptionKey")) { unwrapResult = await this.DekProvider.MdeKeyWrapProvider.UnwrapKeyAsync( dekProperties.WrappedDataEncryptionKey, dekProperties.EncryptionKeyWrapMetadata, cancellationToken); } return(unwrapResult); }
/// <inheritdoc/> public override async Task <DataEncryptionKey> FetchDataEncryptionKeyAsync( string id, string encryptionAlgorithm, CancellationToken cancellationToken) { DataEncryptionKeyProperties dataEncryptionKeyProperties = await this.dataEncryptionKeyContainerCore.FetchDataEncryptionKeyPropertiesAsync( id, diagnosticsContext : CosmosDiagnosticsContext.Create(null), cancellationToken : cancellationToken); // supports Encryption with MDE based algorithm using Legacy Encryption Algorithm Configured DEK. if (string.Equals(encryptionAlgorithm, CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized) && string.Equals(dataEncryptionKeyProperties.EncryptionAlgorithm, CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized)) { return(await this.dataEncryptionKeyContainerCore.FetchUnWrappedMdeSupportedLegacyDekAsync( dataEncryptionKeyProperties, cancellationToken)); } // supports Encryption with Legacy based algorithm using Mde Encryption Algorithm Configured DEK. if (string.Equals(encryptionAlgorithm, CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized) && string.Equals(dataEncryptionKeyProperties.EncryptionAlgorithm, CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized)) { return(await this.dataEncryptionKeyContainerCore.FetchUnWrappedLegacySupportedMdeDekAsync( dataEncryptionKeyProperties, encryptionAlgorithm, diagnosticsContext : CosmosDiagnosticsContext.Create(null), cancellationToken)); } InMemoryRawDek inMemoryRawDek = await this.dataEncryptionKeyContainerCore.FetchUnwrappedAsync( dataEncryptionKeyProperties, diagnosticsContext : CosmosDiagnosticsContext.Create(null), cancellationToken : cancellationToken); return(inMemoryRawDek.DataEncryptionKey); }
/// <summary> /// For providing support to Encrypt/Decrypt items using Legacy DEK with MDE based algorithm. /// UnWrap using Legacy Key Provider and Init MDE Encryption Algorithm with Unwrapped Key. /// </summary> /// <param name="dekProperties"> DEK Properties </param> /// <param name="cancellationToken"> Cancellation Token </param> /// <returns> Data Encryption Key </returns> internal async Task <DataEncryptionKey> FetchUnWrappedMdeSupportedLegacyDekAsync( DataEncryptionKeyProperties dekProperties, CancellationToken cancellationToken) { if (this.DekProvider.EncryptionKeyWrapProvider == null) { throw new InvalidOperationException($"For use of '{CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized}' algorithm based DEK, " + "Encryptor or CosmosDataEncryptionKeyProvider needs to be initialized with EncryptionKeyWrapProvider."); } EncryptionKeyUnwrapResult unwrapResult; try { // unwrap with original wrap provider unwrapResult = await this.DekProvider.EncryptionKeyWrapProvider.UnwrapKeyAsync( dekProperties.WrappedDataEncryptionKey, dekProperties.EncryptionKeyWrapMetadata, cancellationToken); } catch (Exception exception) { throw EncryptionExceptionFactory.EncryptionKeyNotFoundException( $"Failed to unwrap Data Encryption Key with id: '{dekProperties.Id}'.", exception); } // Init PlainDataEncryptionKey and then Init MDE Algorithm using PlaintextDataEncryptionKey. // PlaintextDataEncryptionKey derives DataEncryptionkey to Init a Raw Root Key received via Legacy WrapProvider Unwrap result. PlaintextDataEncryptionKey plaintextDataEncryptionKey = new PlaintextDataEncryptionKey( dekProperties.EncryptionKeyWrapMetadata.GetName(dekProperties.EncryptionKeyWrapMetadata), unwrapResult.DataEncryptionKey); return(new MdeEncryptionAlgorithm( plaintextDataEncryptionKey, Data.Encryption.Cryptography.EncryptionType.Randomized)); }
public async Task <InMemoryRawDek> GetOrAddRawDekAsync( DataEncryptionKeyProperties dekProperties, Func <DataEncryptionKeyProperties, CosmosDiagnosticsContext, CancellationToken, Task <InMemoryRawDek> > unwrapper, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { InMemoryRawDek inMemoryRawDek = await this.RawDekCache.GetAsync( dekProperties.SelfLink, null, () => unwrapper(dekProperties, diagnosticsContext, cancellationToken), cancellationToken); if (inMemoryRawDek.RawDekExpiry <= DateTime.UtcNow) { inMemoryRawDek = await this.RawDekCache.GetAsync( dekProperties.SelfLink, null, () => unwrapper(dekProperties, diagnosticsContext, cancellationToken), cancellationToken, forceRefresh : true); } return(inMemoryRawDek); }
public override async Task <ItemResponse <DataEncryptionKeyProperties> > CreateDataEncryptionKeyAsync( string id, string encryptionAlgorithm, EncryptionKeyWrapMetadata encryptionKeyWrapMetadata, ItemRequestOptions requestOptions = null, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(id)) { throw new ArgumentNullException(nameof(id)); } if (!CosmosEncryptionAlgorithm.VerifyIfSupportedAlgorithm(encryptionAlgorithm)) { throw new ArgumentException(string.Format("Unsupported Encryption Algorithm {0}", encryptionAlgorithm), nameof(encryptionAlgorithm)); } if (encryptionKeyWrapMetadata == null) { throw new ArgumentNullException(nameof(encryptionKeyWrapMetadata)); } CosmosDiagnosticsContext diagnosticsContext = CosmosDiagnosticsContext.Create(requestOptions); byte[] wrappedDek = null; EncryptionKeyWrapMetadata updatedMetadata = null; InMemoryRawDek inMemoryRawDek = null; if (string.Equals(encryptionAlgorithm, CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized)) { (wrappedDek, updatedMetadata, inMemoryRawDek) = await this.GenerateAndWrapRawDekForLegacyEncAlgoAsync( id, encryptionAlgorithm, encryptionKeyWrapMetadata, diagnosticsContext, cancellationToken); } else if (string.Equals(encryptionAlgorithm, CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized)) { (wrappedDek, updatedMetadata) = this.GenerateAndWrapPdekForMdeEncAlgo(encryptionKeyWrapMetadata); } DataEncryptionKeyProperties dekProperties = new DataEncryptionKeyProperties( id, encryptionAlgorithm, wrappedDek, updatedMetadata, DateTime.UtcNow); ItemResponse <DataEncryptionKeyProperties> dekResponse = await this.DekProvider.Container.CreateItemAsync( dekProperties, new PartitionKey(dekProperties.Id), cancellationToken : cancellationToken); this.DekProvider.DekCache.SetDekProperties(id, dekResponse.Resource); if (string.Equals(encryptionAlgorithm, CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized)) { this.DekProvider.DekCache.SetRawDek(id, inMemoryRawDek); } return(dekResponse); }
/// <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); }
public void SetDekProperties(string dekId, DataEncryptionKeyProperties dekProperties) { CachedDekProperties cachedDekProperties = new CachedDekProperties(dekProperties, DateTime.UtcNow + this.dekPropertiesTimeToLive); this.DekPropertiesCache.Set(dekId, cachedDekProperties); }