summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crypto/src/cms/CMSTypedStream.cs66
-rw-r--r--crypto/src/crypto/io/CipherStream.cs44
-rw-r--r--crypto/src/crypto/io/DigestStream.cs31
-rw-r--r--crypto/src/crypto/io/MacStream.cs31
-rw-r--r--crypto/src/crypto/io/SignerStream.cs31
-rw-r--r--crypto/src/openpgp/PgpUtilities.cs38
-rw-r--r--crypto/src/tls/TlsStream.cs26
-rw-r--r--crypto/src/util/io/BaseInputStream.cs49
-rw-r--r--crypto/src/util/io/BaseOutputStream.cs29
-rw-r--r--crypto/src/util/io/BufferedFilterStream.cs18
-rw-r--r--crypto/src/util/io/FilterStream.cs16
-rw-r--r--crypto/src/util/io/LimitedInputStream.cs2
-rw-r--r--crypto/src/util/io/PushbackStream.cs33
-rw-r--r--crypto/src/util/io/Streams.cs169
14 files changed, 445 insertions, 138 deletions
diff --git a/crypto/src/cms/CMSTypedStream.cs b/crypto/src/cms/CMSTypedStream.cs
index 624833848..4cd8014aa 100644
--- a/crypto/src/cms/CMSTypedStream.cs
+++ b/crypto/src/cms/CMSTypedStream.cs
@@ -1,4 +1,3 @@
-using System;
 using System.IO;
 
 using Org.BouncyCastle.Asn1.Pkcs;
@@ -6,69 +5,34 @@ using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Cms
 {
-	public class CmsTypedStream
+    public class CmsTypedStream
 	{
-		private const int BufferSize = 32 * 1024;
+		private readonly string	m_oid;
+		private readonly Stream	m_in;
 
-		private readonly string	_oid;
-		private readonly Stream	_in;
-
-		public CmsTypedStream(
-			Stream inStream)
-			: this(PkcsObjectIdentifiers.Data.Id, inStream, BufferSize)
+		public CmsTypedStream(Stream inStream)
+			: this(PkcsObjectIdentifiers.Data.Id, inStream)
 		{
 		}
 
-		public CmsTypedStream(
-			string oid,
-			Stream inStream)
-			: this(oid, inStream, BufferSize)
+		public CmsTypedStream(string oid, Stream inStream)
+			: this(oid, inStream, Streams.DefaultBufferSize)
 		{
 		}
 
-		public CmsTypedStream(
-			string	oid,
-			Stream	inStream,
-			int		bufSize)
+		public CmsTypedStream(string oid, Stream inStream, int bufSize)
 		{
-			_oid = oid;
-			_in = new FullReaderStream(new BufferedStream(inStream, bufSize));
-		}
+			m_oid = oid;
+            m_in = new BufferedFilterStream(inStream, bufSize);
+        }
 
-		public string ContentType
-		{
-			get { return _oid; }
-		}
+		public string ContentType => m_oid;
 
-		public Stream ContentStream
-		{
-			get { return _in; }
-		}
+		public Stream ContentStream => m_in;
 
 		public void Drain()
 		{
-			Streams.Drain(_in);
-            _in.Dispose();
+			using (m_in) Streams.Drain(m_in);
 		}
-
-		private class FullReaderStream : FilterStream
-		{
-			internal FullReaderStream(Stream input)
-				: base(input)
-			{
-			}
-
-            public override int Read(byte[]	buf, int off, int len)
-			{
-				return Streams.ReadFully(s, buf, off, len);
-			}
-
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-            public override int Read(Span<byte> buffer)
-            {
-                return Streams.ReadFully(s, buffer);
-            }
-#endif
-        }
-    }
+	}
 }
diff --git a/crypto/src/crypto/io/CipherStream.cs b/crypto/src/crypto/io/CipherStream.cs
index fb821999f..5367640c6 100644
--- a/crypto/src/crypto/io/CipherStream.cs
+++ b/crypto/src/crypto/io/CipherStream.cs
@@ -1,9 +1,8 @@
 using System;
-#if NETCOREAPP1_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-using System.Buffers;
-#endif
 using System.Diagnostics;
 using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
 
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.IO;
@@ -58,17 +57,15 @@ namespace Org.BouncyCastle.Crypto.IO
 #if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         public override void CopyTo(Stream destination, int bufferSize)
         {
-            if (m_readCipher == null)
-            {
-                m_stream.CopyTo(destination, bufferSize);
-            }
-            else
-            {
-                base.CopyTo(destination, bufferSize);
-            }
+            Streams.CopyTo(ReadSource, destination, bufferSize);
         }
 #endif
 
+        public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
+        {
+            return Streams.CopyToAsync(ReadSource, destination, bufferSize, cancellationToken);
+        }
+
         public override void Flush()
         {
             m_stream.Flush();
@@ -137,6 +134,11 @@ namespace Org.BouncyCastle.Crypto.IO
 
             return num;
         }
+
+        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
+        {
+            return Streams.ReadAsync(ReadSource, buffer, cancellationToken);
+        }
 #endif
 
         public override int ReadByte()
@@ -181,11 +183,7 @@ namespace Org.BouncyCastle.Crypto.IO
             byte[] output = null;
             if (outputSize > 0)
             {
-#if NETCOREAPP1_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-                output = ArrayPool<byte>.Shared.Rent(outputSize);
-#else
                 output = new byte[outputSize];
-#endif
             }
 
             try
@@ -200,11 +198,7 @@ namespace Org.BouncyCastle.Crypto.IO
             {
                 if (output != null)
                 {
-#if NETCOREAPP1_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-                    ArrayPool<byte>.Shared.Return(output, clearArray: true);
-#else
                     Array.Clear(output, 0, output.Length);
-#endif
                 }
             }
         }
@@ -226,7 +220,7 @@ namespace Org.BouncyCastle.Crypto.IO
             byte[] output = null;
             if (outputSize > 0)
             {
-                output = ArrayPool<byte>.Shared.Rent(outputSize);
+                output = new byte[outputSize];
             }
 
             try
@@ -241,10 +235,15 @@ namespace Org.BouncyCastle.Crypto.IO
             {
                 if (output != null)
                 {
-                    ArrayPool<byte>.Shared.Return(output, clearArray: true);
+                    Array.Clear(output, 0, output.Length);
                 }
             }
         }
+
+        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
+        {
+            return Streams.WriteAsync(WriteDestination, buffer, cancellationToken);
+        }
 #endif
 
         public override void WriteByte(byte value)
@@ -337,5 +336,8 @@ namespace Org.BouncyCastle.Crypto.IO
 
             return bytes;
         }
+
+        private Stream ReadSource => m_readCipher == null ? m_stream : this;
+        private Stream WriteDestination => m_writeCipher == null ? m_stream : this;
     }
 }
diff --git a/crypto/src/crypto/io/DigestStream.cs b/crypto/src/crypto/io/DigestStream.cs
index e57a2f851..56e674389 100644
--- a/crypto/src/crypto/io/DigestStream.cs
+++ b/crypto/src/crypto/io/DigestStream.cs
@@ -1,5 +1,9 @@
 using System;
 using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Crypto.IO
 {
@@ -39,17 +43,15 @@ namespace Org.BouncyCastle.Crypto.IO
 #if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         public override void CopyTo(Stream destination, int bufferSize)
         {
-            if (m_readDigest == null)
-            {
-                m_stream.CopyTo(destination, bufferSize);
-            }
-            else
-            {
-                base.CopyTo(destination, bufferSize);
-            }
+            Streams.CopyTo(ReadSource, destination, bufferSize);
         }
 #endif
 
+        public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
+        {
+            return Streams.CopyToAsync(ReadSource, destination, bufferSize, cancellationToken);
+        }
+
         public override void Flush()
         {
             m_stream.Flush();
@@ -90,6 +92,11 @@ namespace Org.BouncyCastle.Crypto.IO
 
             return n;
         }
+
+        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
+        {
+            return Streams.ReadAsync(ReadSource, buffer, cancellationToken);
+        }
 #endif
 
         public override int ReadByte()
@@ -134,6 +141,11 @@ namespace Org.BouncyCastle.Crypto.IO
                 m_writeDigest.BlockUpdate(buffer);
             }
         }
+
+        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
+        {
+            return Streams.WriteAsync(WriteDestination, buffer, cancellationToken);
+        }
 #endif
 
         public override void WriteByte(byte value)
@@ -154,5 +166,8 @@ namespace Org.BouncyCastle.Crypto.IO
             }
             base.Dispose(disposing);
         }
+
+        private Stream ReadSource => m_readDigest == null ? m_stream : this;
+        private Stream WriteDestination => m_writeDigest == null ? m_stream : this;
     }
 }
diff --git a/crypto/src/crypto/io/MacStream.cs b/crypto/src/crypto/io/MacStream.cs
index bf144bf82..49e207186 100644
--- a/crypto/src/crypto/io/MacStream.cs
+++ b/crypto/src/crypto/io/MacStream.cs
@@ -1,5 +1,9 @@
 using System;
 using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Crypto.IO
 {
@@ -39,17 +43,15 @@ namespace Org.BouncyCastle.Crypto.IO
 #if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         public override void CopyTo(Stream destination, int bufferSize)
         {
-            if (m_readMac == null)
-            {
-                m_stream.CopyTo(destination, bufferSize);
-            }
-            else
-            {
-                base.CopyTo(destination, bufferSize);
-            }
+            Streams.CopyTo(ReadSource, destination, bufferSize);
         }
 #endif
 
+        public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
+        {
+            return Streams.CopyToAsync(ReadSource, destination, bufferSize, cancellationToken);
+        }
+
         public override void Flush()
         {
             m_stream.Flush();
@@ -90,6 +92,11 @@ namespace Org.BouncyCastle.Crypto.IO
 
             return n;
         }
+
+        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
+        {
+            return Streams.ReadAsync(ReadSource, buffer, cancellationToken);
+        }
 #endif
 
         public override int ReadByte()
@@ -134,6 +141,11 @@ namespace Org.BouncyCastle.Crypto.IO
                 m_writeMac.BlockUpdate(buffer);
             }
         }
+
+        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
+        {
+            return Streams.WriteAsync(WriteDestination, buffer, cancellationToken);
+        }
 #endif
 
         public override void WriteByte(byte value)
@@ -154,5 +166,8 @@ namespace Org.BouncyCastle.Crypto.IO
             }
             base.Dispose(disposing);
         }
+
+        private Stream ReadSource => m_readMac == null ? m_stream : this;
+        private Stream WriteDestination => m_writeMac == null ? m_stream : this;
     }
 }
diff --git a/crypto/src/crypto/io/SignerStream.cs b/crypto/src/crypto/io/SignerStream.cs
index e527e5450..78fa69363 100644
--- a/crypto/src/crypto/io/SignerStream.cs
+++ b/crypto/src/crypto/io/SignerStream.cs
@@ -1,5 +1,9 @@
 using System;
 using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Crypto.IO
 {
@@ -39,17 +43,15 @@ namespace Org.BouncyCastle.Crypto.IO
 #if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         public override void CopyTo(Stream destination, int bufferSize)
         {
-            if (m_readSigner == null)
-            {
-                m_stream.CopyTo(destination, bufferSize);
-            }
-            else
-            {
-                base.CopyTo(destination, bufferSize);
-            }
+            Streams.CopyTo(ReadSource, destination, bufferSize);
         }
 #endif
 
+        public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
+        {
+            return Streams.CopyToAsync(ReadSource, destination, bufferSize, cancellationToken);
+        }
+
         public override void Flush()
         {
             m_stream.Flush();
@@ -90,6 +92,11 @@ namespace Org.BouncyCastle.Crypto.IO
 
             return n;
         }
+
+        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
+        {
+            return Streams.ReadAsync(ReadSource, buffer, cancellationToken);
+        }
 #endif
 
         public override int ReadByte()
@@ -134,6 +141,11 @@ namespace Org.BouncyCastle.Crypto.IO
                 m_writeSigner.BlockUpdate(buffer);
             }
         }
+
+        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
+        {
+            return Streams.WriteAsync(WriteDestination, buffer, cancellationToken);
+        }
 #endif
 
         public override void WriteByte(byte value)
@@ -154,5 +166,8 @@ namespace Org.BouncyCastle.Crypto.IO
             }
             base.Dispose(disposing);
         }
+
+        private Stream ReadSource => m_readSigner == null ? m_stream : this;
+        private Stream WriteDestination => m_writeSigner == null ? m_stream : this;
     }
 }
diff --git a/crypto/src/openpgp/PgpUtilities.cs b/crypto/src/openpgp/PgpUtilities.cs
index 03bc73a21..17e100bff 100644
--- a/crypto/src/openpgp/PgpUtilities.cs
+++ b/crypto/src/openpgp/PgpUtilities.cs
@@ -1,7 +1,4 @@
 using System;
-#if NETCOREAPP1_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-using System.Buffers;
-#endif
 using System.Collections.Generic;
 using System.IO;
 using System.Text;
@@ -17,6 +14,7 @@ using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Bcpg.OpenPgp
 {
@@ -414,7 +412,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             PgpLiteralDataGenerator lData = new PgpLiteralDataGenerator();
             using (var pOut = lData.Open(output, fileType, file.Name, file.Length, file.LastWriteTime))
             {
-                PipeFileContents(file, pOut, 32768);
+                PipeFileContents(file, pOut);
             }
         }
 
@@ -428,32 +426,16 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
         }
 
-		private static void PipeFileContents(FileInfo file, Stream pOut, int bufSize)
-		{
-#if NETCOREAPP1_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-            byte[] buf = ArrayPool<byte>.Shared.Rent(bufSize);
-#else
-            byte[] buf = new byte[bufSize];
-#endif
+        private static void PipeFileContents(FileInfo file, Stream pOut)
+        {
+            PipeFileContents(file, pOut, Streams.DefaultBufferSize);
+        }
 
-            try
-            {
-                using (var fileStream = file.OpenRead())
-                {
-                    int len;
-                    while ((len = fileStream.Read(buf, 0, buf.Length)) > 0)
-                    {
-                        pOut.Write(buf, 0, len);
-                    }
-                }
-            }
-            finally
+        private static void PipeFileContents(FileInfo file, Stream pOut, int bufferSize)
+		{
+            using (var fileStream = file.OpenRead())
             {
-#if NETCOREAPP1_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-                ArrayPool<byte>.Shared.Return(buf, clearArray: true);
-#else
-                Array.Clear(buf, 0, buf.Length);
-#endif
+                Streams.CopyTo(fileStream, pOut, bufferSize);
             }
         }
 
diff --git a/crypto/src/tls/TlsStream.cs b/crypto/src/tls/TlsStream.cs
index 5c07da2bf..d7d79a6fa 100644
--- a/crypto/src/tls/TlsStream.cs
+++ b/crypto/src/tls/TlsStream.cs
@@ -1,5 +1,9 @@
 using System;
 using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Tls
 {
@@ -28,6 +32,18 @@ namespace Org.BouncyCastle.Tls
             get { return true; }
         }
 
+#if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void CopyTo(Stream destination, int bufferSize)
+        {
+            Streams.CopyTo(this, destination, bufferSize);
+        }
+#endif
+
+        public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
+        {
+            return Streams.CopyToAsync(this, destination, bufferSize, cancellationToken);
+        }
+
         protected override void Dispose(bool disposing)
         {
             if (disposing)
@@ -63,6 +79,11 @@ namespace Org.BouncyCastle.Tls
         {
             return m_handler.ReadApplicationData(buffer);
         }
+
+        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
+        {
+            return Streams.ReadAsync(this, buffer, cancellationToken);
+        }
 #endif
 
         public override int ReadByte()
@@ -92,6 +113,11 @@ namespace Org.BouncyCastle.Tls
         {
             m_handler.WriteApplicationData(buffer);
         }
+
+        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
+        {
+            return Streams.WriteAsync(this, buffer, cancellationToken);
+        }
 #endif
 
         public override void WriteByte(byte value)
diff --git a/crypto/src/util/io/BaseInputStream.cs b/crypto/src/util/io/BaseInputStream.cs
index eaaf9556d..8f5265fee 100644
--- a/crypto/src/util/io/BaseInputStream.cs
+++ b/crypto/src/util/io/BaseInputStream.cs
@@ -1,5 +1,7 @@
 using System;
 using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
 
 namespace Org.BouncyCastle.Utilities.IO
 {
@@ -10,6 +12,18 @@ namespace Org.BouncyCastle.Utilities.IO
         public sealed override bool CanSeek { get { return false; } }
         public sealed override bool CanWrite { get { return false; } }
 
+#if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void CopyTo(Stream destination, int bufferSize)
+        {
+            Streams.CopyTo(this, destination, bufferSize);
+        }
+#endif
+
+        public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
+        {
+            return Streams.CopyToAsync(this, destination, bufferSize, cancellationToken);
+        }
+
         public sealed override void Flush() {}
         public sealed override long Length { get { throw new NotSupportedException(); } }
         public sealed override long Position
@@ -42,12 +56,47 @@ namespace Org.BouncyCastle.Utilities.IO
             return pos;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int Read(Span<byte> buffer)
+        {
+            int count = buffer.Length, pos = 0;
+            try
+            {
+                while (pos < count)
+                {
+                    int b = ReadByte();
+                    if (b < 0)
+                        break;
+
+                    buffer[pos++] = (byte)b;
+                }
+            }
+            catch (IOException)
+            {
+                if (pos == 0)
+                    throw;
+            }
+            return pos;
+        }
+
+        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
+        {
+            return Streams.ReadAsync(this, buffer, cancellationToken);
+        }
+#endif
+
         public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
         public sealed override void SetLength(long value) { throw new NotSupportedException(); }
         public sealed override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        // TODO[api] sealed
         public override void Write(ReadOnlySpan<byte> buffer) { throw new NotSupportedException(); }
+        // TODO[api] sealed
+        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
+        {
+            throw new NotSupportedException();
+        }
 #endif
     }
 }
diff --git a/crypto/src/util/io/BaseOutputStream.cs b/crypto/src/util/io/BaseOutputStream.cs
index 0fc8e9681..87a850c35 100644
--- a/crypto/src/util/io/BaseOutputStream.cs
+++ b/crypto/src/util/io/BaseOutputStream.cs
@@ -1,5 +1,7 @@
 using System;
 using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
 
 namespace Org.BouncyCastle.Utilities.IO
 {
@@ -11,8 +13,14 @@ namespace Org.BouncyCastle.Utilities.IO
         public sealed override bool CanWrite { get { return true; } }
 
 #if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        // TODO[api] sealed
         public override void CopyTo(Stream destination, int bufferSize) { throw new NotSupportedException(); }
 #endif
+        // TODO[api] sealed
+        public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
+        {
+            throw new NotSupportedException();
+        }
         public override void Flush() {}
         public sealed override long Length { get { throw new NotSupportedException(); } }
         public sealed override long Position
@@ -23,6 +31,11 @@ namespace Org.BouncyCastle.Utilities.IO
         public sealed override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); }
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         public sealed override int Read(Span<byte> buffer) { throw new NotSupportedException(); }
+        // TODO[api] sealed
+        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
+        {
+            throw new NotSupportedException();
+        }
 #endif
         public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
         public sealed override void SetLength(long value) { throw new NotSupportedException(); }
@@ -37,6 +50,22 @@ namespace Org.BouncyCastle.Utilities.IO
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void Write(ReadOnlySpan<byte> buffer)
+        {
+            int count = buffer.Length;
+            for (int i = 0; i < count; ++i)
+            {
+                WriteByte(buffer[i]);
+            }
+        }
+
+        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
+        {
+            return Streams.WriteAsync(this, buffer, cancellationToken);
+        }
+#endif
+
         public virtual void Write(params byte[] buffer)
         {
             Write(buffer, 0, buffer.Length);
diff --git a/crypto/src/util/io/BufferedFilterStream.cs b/crypto/src/util/io/BufferedFilterStream.cs
new file mode 100644
index 000000000..3c2a4856c
--- /dev/null
+++ b/crypto/src/util/io/BufferedFilterStream.cs
@@ -0,0 +1,18 @@
+using System.IO;
+
+namespace Org.BouncyCastle.Utilities.IO
+{
+    internal sealed class BufferedFilterStream
+        : FilterStream
+    {
+        internal BufferedFilterStream(Stream s)
+            : this(s, Streams.DefaultBufferSize)
+        {
+        }
+
+        internal BufferedFilterStream(Stream s, int bufferSize)
+            : base(new BufferedStream(s, bufferSize))
+        {
+        }
+    }
+}
diff --git a/crypto/src/util/io/FilterStream.cs b/crypto/src/util/io/FilterStream.cs
index 38077edd2..630bdc22f 100644
--- a/crypto/src/util/io/FilterStream.cs
+++ b/crypto/src/util/io/FilterStream.cs
@@ -1,5 +1,7 @@
 using System;
 using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
 
 namespace Org.BouncyCastle.Utilities.IO
 {
@@ -27,9 +29,13 @@ namespace Org.BouncyCastle.Utilities.IO
 #if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         public override void CopyTo(Stream destination, int bufferSize)
         {
-            s.CopyTo(destination, bufferSize);
+            Streams.CopyTo(s, destination, bufferSize);
         }
 #endif
+        public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
+        {
+            return Streams.CopyToAsync(s, destination, bufferSize, cancellationToken);
+        }
         public override void Flush()
         {
             s.Flush();
@@ -52,6 +58,10 @@ namespace Org.BouncyCastle.Utilities.IO
         {
             return s.Read(buffer);
         }
+        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
+        {
+            return Streams.ReadAsync(s, buffer, cancellationToken);
+        }
 #endif
         public override int ReadByte()
         {
@@ -74,6 +84,10 @@ namespace Org.BouncyCastle.Utilities.IO
         {
             s.Write(buffer);
         }
+        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
+        {
+            return Streams.WriteAsync(s, buffer, cancellationToken);
+        }
 #endif
         public override void WriteByte(byte value)
         {
diff --git a/crypto/src/util/io/LimitedInputStream.cs b/crypto/src/util/io/LimitedInputStream.cs
index 4c6aac631..2f19ef996 100644
--- a/crypto/src/util/io/LimitedInputStream.cs
+++ b/crypto/src/util/io/LimitedInputStream.cs
@@ -3,7 +3,7 @@ using System.IO;
 
 namespace Org.BouncyCastle.Utilities.IO
 {
-    internal class LimitedInputStream
+    internal sealed class LimitedInputStream
         : BaseInputStream
     {
         private readonly Stream m_stream;
diff --git a/crypto/src/util/io/PushbackStream.cs b/crypto/src/util/io/PushbackStream.cs
index 15ba70501..be6b26d4c 100644
--- a/crypto/src/util/io/PushbackStream.cs
+++ b/crypto/src/util/io/PushbackStream.cs
@@ -1,5 +1,7 @@
 using System;
 using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
 
 namespace Org.BouncyCastle.Utilities.IO
 {
@@ -22,10 +24,22 @@ namespace Org.BouncyCastle.Utilities.IO
                 m_buf = -1;
             }
 
-            s.CopyTo(destination, bufferSize);
+			Streams.CopyTo(s, destination, bufferSize);
         }
 #endif
 
+        public override async Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
+        {
+            if (m_buf != -1)
+            {
+				byte[] buffer = new byte[1]{ (byte)m_buf };
+                await destination.WriteAsync(buffer, 0, 1, cancellationToken).ConfigureAwait(false);
+                m_buf = -1;
+            }
+
+            await Streams.CopyToAsync(s, destination, bufferSize, cancellationToken);
+        }
+
         public override int Read(byte[] buffer, int offset, int count)
 		{
 			Streams.ValidateBufferArguments(buffer, offset, count);
@@ -58,6 +72,21 @@ namespace Org.BouncyCastle.Utilities.IO
 
             return s.Read(buffer);
         }
+
+        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
+        {
+            if (m_buf != -1)
+            {
+                if (buffer.IsEmpty)
+                    return new ValueTask<int>(0);
+
+                buffer.Span[0] = (byte)m_buf;
+                m_buf = -1;
+                return new ValueTask<int>(1);
+            }
+
+            return Streams.ReadAsync(s, buffer, cancellationToken);
+        }
 #endif
 
         public override int ReadByte()
@@ -69,7 +98,7 @@ namespace Org.BouncyCastle.Utilities.IO
 				return tmp;
 			}
 
-			return base.ReadByte();
+			return s.ReadByte();
 		}
 
 		public virtual void Unread(int b)
diff --git a/crypto/src/util/io/Streams.cs b/crypto/src/util/io/Streams.cs
index da8f01068..b975d03bd 100644
--- a/crypto/src/util/io/Streams.cs
+++ b/crypto/src/util/io/Streams.cs
@@ -1,15 +1,78 @@
 using System;
 using System.IO;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
 
 namespace Org.BouncyCastle.Utilities.IO
 {
-	public static class Streams
+    public static class Streams
 	{
-		private const int BufferSize = 4096;
+        private static readonly int MaxStackAlloc = Environment.Is64BitProcess ? 4096 : 1024;
 
-		public static void Drain(Stream inStr)
+		public static int DefaultBufferSize => MaxStackAlloc;
+
+        public static void CopyTo(Stream source, Stream destination)
+        {
+			CopyTo(source, destination, DefaultBufferSize);
+        }
+
+        public static void CopyTo(Stream source, Stream destination, int bufferSize)
+        {
+            int bytesRead;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> buffer = bufferSize <= MaxStackAlloc
+				? stackalloc byte[bufferSize]
+				: new byte[bufferSize];
+			while ((bytesRead = source.Read(buffer)) != 0)
+			{
+				destination.Write(buffer[..bytesRead]);
+			}
+#else
+			byte[] buffer = new byte[bufferSize];
+			while ((bytesRead = source.Read(buffer, 0, buffer.Length)) != 0)
+			{
+			    destination.Write(buffer, 0, bytesRead);
+			}
+#endif
+		}
+
+        public static Task CopyToAsync(Stream source, Stream destination)
+        {
+            return CopyToAsync(source, destination, DefaultBufferSize);
+        }
+
+        public static Task CopyToAsync(Stream source, Stream destination, int bufferSize)
+        {
+            return CopyToAsync(source, destination, bufferSize, CancellationToken.None);
+        }
+
+        public static Task CopyToAsync(Stream source, Stream destination, CancellationToken cancellationToken)
+        {
+            return CopyToAsync(source, destination, DefaultBufferSize, cancellationToken);
+        }
+
+        public static async Task CopyToAsync(Stream source, Stream destination, int bufferSize,
+			CancellationToken cancellationToken)
+        {
+            int bytesRead;
+            byte[] buffer = new byte[bufferSize];
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            while ((bytesRead = await ReadAsync(source, new Memory<byte>(buffer), cancellationToken).ConfigureAwait(false)) != 0)
+			{
+				await WriteAsync(destination, new ReadOnlyMemory<byte>(buffer, 0, bytesRead), cancellationToken).ConfigureAwait(false);
+			}
+#else
+			while ((bytesRead = await ReadAsync(source, buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
+			{
+				await WriteAsync(destination, buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
+			}
+#endif
+		}
+
+        public static void Drain(Stream inStr)
 		{
-			inStr.CopyTo(Stream.Null, BufferSize);
+			CopyTo(inStr, Stream.Null, DefaultBufferSize);
 		}
 
         /// <summary>Write the full contents of inStr to the destination stream outStr.</summary>
@@ -18,7 +81,7 @@ namespace Org.BouncyCastle.Utilities.IO
         /// <exception cref="IOException">In case of IO failure.</exception>
         public static void PipeAll(Stream inStr, Stream outStr)
 		{
-			inStr.CopyTo(outStr, BufferSize);
+            PipeAll(inStr, outStr, DefaultBufferSize);
         }
 
         /// <summary>Write the full contents of inStr to the destination stream outStr.</summary>
@@ -28,7 +91,7 @@ namespace Org.BouncyCastle.Utilities.IO
         /// <exception cref="IOException">In case of IO failure.</exception>
         public static void PipeAll(Stream inStr, Stream outStr, int bufferSize)
         {
-            inStr.CopyTo(outStr, bufferSize);
+            CopyTo(inStr, outStr, bufferSize);
 		}
 
 		/// <summary>
@@ -48,12 +111,17 @@ namespace Org.BouncyCastle.Utilities.IO
 		/// <exception cref="IOException"></exception>
 		public static long PipeAllLimited(Stream inStr, long limit, Stream outStr)
 		{
-			var limited = new LimitedInputStream(inStr, limit);
-            limited.CopyTo(outStr, BufferSize);
-			return limit - limited.CurrentLimit;
+			return PipeAllLimited(inStr, limit, outStr, DefaultBufferSize);
 		}
 
-		public static byte[] ReadAll(Stream inStr)
+        public static long PipeAllLimited(Stream inStr, long limit, Stream outStr, int bufferSize)
+        {
+            var limited = new LimitedInputStream(inStr, limit);
+            CopyTo(limited, outStr, bufferSize);
+            return limit - limited.CurrentLimit;
+        }
+
+        public static byte[] ReadAll(Stream inStr)
 		{
 			MemoryStream buf = new MemoryStream();
 			PipeAll(inStr, buf);
@@ -72,6 +140,48 @@ namespace Org.BouncyCastle.Utilities.IO
 			return buf.ToArray();
 		}
 
+        public static Task<int> ReadAsync(Stream source, byte[] buffer, int offset, int count)
+        {
+            return source.ReadAsync(buffer, offset, count);
+        }
+
+        public static Task<int> ReadAsync(Stream source, byte[] buffer, int offset, int count,
+            CancellationToken cancellationToken)
+        {
+            return source.ReadAsync(buffer, offset, count, cancellationToken);
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public static ValueTask<int> ReadAsync(Stream source, Memory<byte> buffer,
+			CancellationToken cancellationToken = default)
+        {
+            if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array))
+            {
+                return new ValueTask<int>(
+                    ReadAsync(source, array.Array!, array.Offset, array.Count, cancellationToken));
+            }
+
+            byte[] sharedBuffer = new byte[buffer.Length];
+			var readTask = ReadAsync(source, sharedBuffer, 0, buffer.Length, cancellationToken);
+            return FinishReadAsync(readTask, sharedBuffer, buffer);
+        }
+
+        private static async ValueTask<int> FinishReadAsync(Task<int> readTask, byte[] localBuffer,
+			Memory<byte> localDestination)
+        {
+            try
+            {
+                int result = await readTask.ConfigureAwait(false);
+                new ReadOnlySpan<byte>(localBuffer, 0, result).CopyTo(localDestination.Span);
+                return result;
+            }
+            finally
+            {
+                Array.Fill<byte>(localBuffer, 0x00);
+            }
+        }
+#endif
+
 		public static int ReadFully(Stream inStr, byte[] buf)
 		{
 			return ReadFully(inStr, buf, 0, buf.Length);
@@ -117,6 +227,45 @@ namespace Org.BouncyCastle.Utilities.IO
 				throw new ArgumentOutOfRangeException("count");
 		}
 
+        public static Task WriteAsync(Stream destination, byte[] buffer, int offset, int count)
+        {
+            return destination.WriteAsync(buffer, offset, count);
+        }
+
+        public static Task WriteAsync(Stream destination, byte[] buffer, int offset, int count,
+            CancellationToken cancellationToken)
+        {
+            return destination.WriteAsync(buffer, offset, count, cancellationToken);
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public static ValueTask WriteAsync(Stream destination, ReadOnlyMemory<byte> buffer,
+            CancellationToken cancellationToken = default)
+        {
+            if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array))
+            {
+                return new ValueTask(
+                    WriteAsync(destination, array.Array!, array.Offset, array.Count, cancellationToken));
+            }
+
+            byte[] sharedBuffer = buffer.ToArray();
+            var writeTask = WriteAsync(destination, sharedBuffer, 0, buffer.Length, cancellationToken);
+            return new ValueTask(FinishWriteAsync(writeTask, sharedBuffer));
+        }
+
+        private static async Task FinishWriteAsync(Task writeTask, byte[] localBuffer)
+        {
+            try
+            {
+                await writeTask.ConfigureAwait(false);
+            }
+            finally
+            {
+                Array.Fill<byte>(localBuffer, 0x00);
+            }
+        }
+#endif
+
         /// <exception cref="IOException"></exception>
         public static int WriteBufTo(MemoryStream buf, byte[] output, int offset)
         {