Add CryptoStream.FlushFinalBlockAsync
authorKevin Jones <kevin@vcsjones.com>
Tue, 21 Jul 2020 13:58:36 +0000 (09:58 -0400)
committerGitHub <noreply@github.com>
Tue, 21 Jul 2020 13:58:36 +0000 (06:58 -0700)
src/libraries/System.Security.Cryptography.Primitives/ref/System.Security.Cryptography.Primitives.cs
src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptoStream.cs
src/libraries/System.Security.Cryptography.Primitives/tests/CryptoStream.cs

index 355c6c239b93198adc0b60b9cb1febb763a2ac00..2add4b9ea90d69e132f984903f07783118db91e3 100644 (file)
@@ -82,6 +82,7 @@ namespace System.Security.Cryptography
         public override void Flush() { }
         public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) { throw null; }
         public void FlushFinalBlock() { }
+        public System.Threading.Tasks.ValueTask FlushFinalBlockAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
         public override int Read(byte[] buffer, int offset, int count) { throw null; }
         public override System.Threading.Tasks.Task<int> ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { throw null; }
         public override int ReadByte() { throw null; }
index e414e3c6eb08aa63ec7f0e2edf717cf815372014..2e426cc4185381fd1c5bb8b734a723c6949704d0 100644 (file)
@@ -100,9 +100,23 @@ namespace System.Security.Cryptography
         // byte[] ciphertext = ms.ToArray();
         // cs.Close();
         public void FlushFinalBlock() =>
-            FlushFinalBlockAsync(useAsync: false).AsTask().GetAwaiter().GetResult();
+            FlushFinalBlockAsync(useAsync: false, default).AsTask().GetAwaiter().GetResult();
+
+        /// <summary>
+        /// Asynchronously updates the underlying data source or repository with the
+        /// current state of the buffer, then clears the buffer.
+        /// </summary>
+        /// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None"/>.</param>
+        /// <returns>A task that represents the asynchronous flush operation.</returns>
+        public ValueTask FlushFinalBlockAsync(CancellationToken cancellationToken = default)
+        {
+            if (cancellationToken.IsCancellationRequested)
+                return ValueTask.FromCanceled(cancellationToken);
+
+            return FlushFinalBlockAsync(useAsync: true, cancellationToken);
+        }
 
-        private async ValueTask FlushFinalBlockAsync(bool useAsync)
+        private async ValueTask FlushFinalBlockAsync(bool useAsync, CancellationToken cancellationToken)
         {
             if (_finalBlockTransformed)
                 throw new NotSupportedException(SR.Cryptography_CryptoStream_FlushFinalBlockTwice);
@@ -116,7 +130,7 @@ namespace System.Security.Cryptography
                 byte[] finalBytes = _transform.TransformFinalBlock(_inputBuffer!, 0, _inputBufferIndex);
                 if (useAsync)
                 {
-                    await _stream.WriteAsync(new ReadOnlyMemory<byte>(finalBytes)).ConfigureAwait(false);
+                    await _stream.WriteAsync(new ReadOnlyMemory<byte>(finalBytes), cancellationToken).ConfigureAwait(false);
                 }
                 else
                 {
@@ -129,14 +143,14 @@ namespace System.Security.Cryptography
             {
                 if (!innerCryptoStream.HasFlushedFinalBlock)
                 {
-                    await innerCryptoStream.FlushFinalBlockAsync(useAsync).ConfigureAwait(false);
+                    await innerCryptoStream.FlushFinalBlockAsync(useAsync, cancellationToken).ConfigureAwait(false);
                 }
             }
             else
             {
                 if (useAsync)
                 {
-                    await _stream.FlushAsync().ConfigureAwait(false);
+                    await _stream.FlushAsync(cancellationToken).ConfigureAwait(false);
                 }
                 else
                 {
@@ -691,7 +705,7 @@ namespace System.Security.Cryptography
             {
                 if (!_finalBlockTransformed)
                 {
-                    await FlushFinalBlockAsync(useAsync: true).ConfigureAwait(false);
+                    await FlushFinalBlockAsync(useAsync: true, default).ConfigureAwait(false);
                 }
 
                 if (!_leaveOpen)
index 1d663a2c4fffdf085a1d0e927417954b21b4db26..6156e9c2ea200b0038755bbe0f1d979738638b41 100644 (file)
@@ -187,6 +187,34 @@ namespace System.Security.Cryptography.Encryption.Tests.Asymmetric
             }
         }
 
+        [Fact]
+        public static async Task FlushFinalBlockAsync()
+        {
+            ICryptoTransform encryptor = new IdentityTransform(1, 1, true);
+            using (MemoryStream output = new MemoryStream())
+            using (CryptoStream encryptStream = new CryptoStream(output, encryptor, CryptoStreamMode.Write))
+            {
+                await encryptStream.WriteAsync(new byte[] { 1, 2, 3, 4, 5 }, 0, 5);
+                await encryptStream.FlushFinalBlockAsync();
+                Assert.True(encryptStream.HasFlushedFinalBlock);
+                Assert.Equal(5, output.ToArray().Length);
+            }
+        }
+
+        [Fact]
+        public static async Task FlushFinalBlockAsync_Cancelled()
+        {
+            ICryptoTransform encryptor = new IdentityTransform(1, 1, true);
+            using (MemoryStream output = new MemoryStream())
+            using (CryptoStream encryptStream = new CryptoStream(output, encryptor, CryptoStreamMode.Write))
+            {
+                await encryptStream.WriteAsync(new byte[] { 1, 2, 3, 4, 5 }, 0, 5);
+                ValueTask waitable = encryptStream.FlushFinalBlockAsync(new Threading.CancellationToken(canceled: true));
+                Assert.True(waitable.IsCanceled);
+                Assert.False(encryptStream.HasFlushedFinalBlock);
+            }
+        }
+
         [Fact]
         public static void FlushCalledOnFlushAsync_DeriveClass()
         {