/// <summary> /// Encrypts the given stream and provides the metadata used to encrypt. This method writes to a memory stream, /// optimized for known-size data that will already be buffered in memory. /// </summary> /// <param name="plaintext">Stream to encrypt.</param> /// <param name="async">Whether to wrap the content encryption key asynchronously.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <returns>The encrypted data and the encryption metadata for the wrapped stream.</returns> public async Task <(byte[] Ciphertext, EncryptionData EncryptionData)> BufferedEncryptInternal( Stream plaintext, bool async, CancellationToken cancellationToken) { ValidateMembers(); var generatedKey = CreateKey(Constants.ClientSideEncryption.EncryptionKeySizeBits); using var gcm = new GcmAuthenticatedCryptographicTransform(generatedKey, TransformMode.Encrypt); EncryptionData encryptionData = await CreateEncryptionDataInternal(generatedKey, async, cancellationToken) .ConfigureAwait(false); var ciphertext = new MemoryStream(); var transformStream = new AuthenticatedRegionCryptoStream( ciphertext, gcm, EncryptionRegionDataSize, CryptoStreamMode.Write); if (async) { // constant comes from parameter documentation const int copyToAsyncDefaultBufferSize = 80 * Constants.KB; await plaintext.CopyToAsync(transformStream, copyToAsyncDefaultBufferSize, cancellationToken).ConfigureAwait(false); } else { plaintext.CopyTo(transformStream); } await transformStream.FlushFinalInternal(async, cancellationToken).ConfigureAwait(false); return(ciphertext.ToArray(), encryptionData); }
/// <summary> /// Creates a crypto transform stream to write blob contents to. /// </summary> /// <param name="openWriteInternal"> /// OpenWrite function that applies <see cref="EncryptionAgent"/> to the operation. /// </param> /// <param name="async"> /// Whether to perform the operation asynchronously. /// </param> /// <param name="cancellationToken"> /// Cancellation token for the operation. /// </param> /// <returns> /// Content transform write stream and encryption metadata. /// </returns> public async Task <Stream> EncryptedOpenWriteInternal( Func <EncryptionData, bool, CancellationToken, Task <Stream> > openWriteInternal, bool async, CancellationToken cancellationToken) { ValidateMembers(); var generatedKey = CreateKey(Constants.ClientSideEncryption.EncryptionKeySizeBits); EncryptionData encryptionData = await CreateEncryptionDataInternal(generatedKey, async, cancellationToken) .ConfigureAwait(false); // transform is disposable but gets disposed by the stream var gcm = new GcmAuthenticatedCryptographicTransform(generatedKey, TransformMode.Encrypt); // TODO this stream has 4MB buffer, openwrite stream has 4MB buffer, can we combine these? Stream writeStream = new AuthenticatedRegionCryptoStream( #pragma warning disable AZC0110 // DO NOT use await keyword in possibly synchronous scope. // analyzer struggles to recognize async pattern with a Func instead of a proper method. await openWriteInternal(encryptionData, async, cancellationToken).ConfigureAwait(false), #pragma warning restore AZC0110 // DO NOT use await keyword in possibly synchronous scope. gcm, EncryptionRegionDataSize, CryptoStreamMode.Write); return(writeStream); }
/// <summary> /// Wraps the given read-stream in a CryptoStream and provides the metadata used to create /// that stream. /// </summary> /// <param name="plaintext">Stream to wrap.</param> /// <param name="async">Whether to wrap the content encryption key asynchronously.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <returns>The wrapped stream to read from and the encryption metadata for the wrapped stream.</returns> public async Task <(Stream Ciphertext, EncryptionData EncryptionData)> EncryptInternal( Stream plaintext, bool async, CancellationToken cancellationToken) { ValidateMembers(); var generatedKey = CreateKey(Constants.ClientSideEncryption.EncryptionKeySizeBits); // transform is disposable but gets disposed by the stream var gcm = new GcmAuthenticatedCryptographicTransform(generatedKey, TransformMode.Encrypt); EncryptionData encryptionData = await CreateEncryptionDataInternal(generatedKey, async, cancellationToken) .ConfigureAwait(false); Stream ciphertext = new AuthenticatedRegionCryptoStream( plaintext, gcm, EncryptionRegionDataSize, CryptoStreamMode.Read); return(ciphertext, encryptionData); }