summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2022-11-01 14:39:54 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-11-01 14:39:54 +0700
commit716a491e3ed312da6c80a74e327d62dd4388b11e (patch)
tree0adabea28431857f372256233ddd4b2e0982190b
parentPackage with LICENSE.md file (diff)
downloadBouncyCastle.NET-ed25519-716a491e3ed312da6c80a74e327d62dd4388b11e.tar.xz
More Span-based Stream methods
-rw-r--r--crypto/src/asn1/Asn1OutputStream.cs16
-rw-r--r--crypto/src/asn1/ConstructedBitStream.cs52
-rw-r--r--crypto/src/asn1/ConstructedOctetStream.cs52
-rw-r--r--crypto/src/asn1/DefiniteLengthInputStream.cs21
-rw-r--r--crypto/src/asn1/IndefiniteLengthInputStream.cs23
-rw-r--r--crypto/src/bcpg/ArmoredInputStream.cs21
-rw-r--r--crypto/src/bcpg/BcpgInputStream.cs41
-rw-r--r--crypto/src/bcpg/BcpgOutputStream.cs112
-rw-r--r--crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs9
-rw-r--r--crypto/src/cms/CMSCompressedDataStreamGenerator.cs10
-rw-r--r--crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs9
-rw-r--r--crypto/src/cms/CMSSignedDataStreamGenerator.cs9
-rw-r--r--crypto/src/cms/CMSTypedStream.cs7
-rw-r--r--crypto/src/openpgp/PgpEncryptedData.cs29
-rw-r--r--crypto/src/openpgp/WrappedGeneratorStream.cs5
-rw-r--r--crypto/src/tls/CombinedHash.cs8
-rw-r--r--crypto/src/tls/DeferredHash.cs16
-rw-r--r--crypto/src/tls/crypto/TlsHash.cs4
-rw-r--r--crypto/src/tls/crypto/TlsHashSink.cs10
-rw-r--r--crypto/src/tls/crypto/TlsMac.cs4
-rw-r--r--crypto/src/tls/crypto/TlsMacSink.cs10
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs7
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcTlsHash.cs7
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs7
-rw-r--r--crypto/src/util/io/BaseInputStream.cs4
-rw-r--r--crypto/src/util/io/BaseOutputStream.cs3
-rw-r--r--crypto/src/util/io/FilterStream.cs23
27 files changed, 462 insertions, 57 deletions
diff --git a/crypto/src/asn1/Asn1OutputStream.cs b/crypto/src/asn1/Asn1OutputStream.cs
index 59178ea31..163e3848c 100644
--- a/crypto/src/asn1/Asn1OutputStream.cs
+++ b/crypto/src/asn1/Asn1OutputStream.cs
@@ -1,5 +1,10 @@
 using System;
 using System.IO;
+using System.Diagnostics;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+using System.Buffers.Binary;
+using System.Numerics;
+#endif
 
 using Org.BouncyCastle.Utilities.IO;
 
@@ -73,15 +78,19 @@ namespace Org.BouncyCastle.Asn1
         {
             if (dl < 128)
             {
+                Debug.Assert(dl >= 0);
                 WriteByte((byte)dl);
                 return;
             }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-            Span<byte> stack = stackalloc byte[5];
+            Span<byte> encoding = stackalloc byte[5];
+            BinaryPrimitives.WriteUInt32BigEndian(encoding[1..], (uint)dl);
+            int leadingZeroBytes = BitOperations.LeadingZeroCount((uint)dl) / 8;
+            encoding[leadingZeroBytes] = (byte)(0x84 - leadingZeroBytes);
+            Write(encoding[leadingZeroBytes..]);
 #else
             byte[] stack = new byte[5];
-#endif
             int pos = stack.Length;
 
             do
@@ -94,9 +103,6 @@ namespace Org.BouncyCastle.Asn1
             int count = stack.Length - pos;
             stack[--pos] = (byte)(0x80 | count);
 
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-            Write(stack[pos..]);
-#else
             Write(stack, pos, count + 1);
 #endif
         }
diff --git a/crypto/src/asn1/ConstructedBitStream.cs b/crypto/src/asn1/ConstructedBitStream.cs
index 49f54fc1b..f089dac75 100644
--- a/crypto/src/asn1/ConstructedBitStream.cs
+++ b/crypto/src/asn1/ConstructedBitStream.cs
@@ -33,6 +33,9 @@ namespace Org.BouncyCastle.Asn1
         {
             Streams.ValidateBufferArguments(buffer, offset, count);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return Read(buffer.AsSpan(offset, count));
+#else
             if (count < 1)
                 return 0;
 
@@ -75,8 +78,57 @@ namespace Org.BouncyCastle.Asn1
                     m_currentStream = m_currentParser.GetBitStream();
                 }
             }
+#endif
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int Read(Span<byte> buffer)
+        {
+            if (buffer.IsEmpty)
+                return 0;
+
+            if (m_currentStream == null)
+            {
+                if (!m_first)
+                    return 0;
+
+                m_currentParser = GetNextParser();
+                if (m_currentParser == null)
+                    return 0;
+
+                m_first = false;
+                m_currentStream = m_currentParser.GetBitStream();
+            }
+
+            int totalRead = 0;
+
+            for (;;)
+            {
+                int numRead = m_currentStream.Read(buffer[totalRead..]);
+
+                if (numRead > 0)
+                {
+                    totalRead += numRead;
+
+                    if (totalRead == buffer.Length)
+                        return totalRead;
+                }
+                else
+                {
+                    m_padBits = m_currentParser.PadBits;
+                    m_currentParser = GetNextParser();
+                    if (m_currentParser == null)
+                    {
+                        m_currentStream = null;
+                        return totalRead;
+                    }
+
+                    m_currentStream = m_currentParser.GetBitStream();
+                }
+            }
+        }
+#endif
+
         public override int ReadByte()
         {
             if (m_currentStream == null)
diff --git a/crypto/src/asn1/ConstructedOctetStream.cs b/crypto/src/asn1/ConstructedOctetStream.cs
index 12aa14e74..d005f9fe7 100644
--- a/crypto/src/asn1/ConstructedOctetStream.cs
+++ b/crypto/src/asn1/ConstructedOctetStream.cs
@@ -1,3 +1,4 @@
+using System;
 using System.IO;
 
 using Org.BouncyCastle.Utilities;
@@ -22,6 +23,9 @@ namespace Org.BouncyCastle.Asn1
 		{
 			Streams.ValidateBufferArguments(buffer, offset, count);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			return Read(buffer.AsSpan(offset, count));
+#else
 			if (count < 1)
                 return 0;
 
@@ -63,8 +67,56 @@ namespace Org.BouncyCastle.Asn1
 					m_currentStream = next.GetOctetStream();
 				}
 			}
+#endif
 		}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public override int Read(Span<byte> buffer)
+		{
+			if (buffer.IsEmpty)
+                return 0;
+
+			if (m_currentStream == null)
+			{
+				if (!m_first)
+					return 0;
+
+                Asn1OctetStringParser next = GetNextParser();
+                if (next == null)
+                    return 0;
+
+				m_first = false;
+				m_currentStream = next.GetOctetStream();
+			}
+
+			int totalRead = 0;
+
+			for (;;)
+			{
+				int numRead = m_currentStream.Read(buffer[totalRead..]);
+
+				if (numRead > 0)
+				{
+					totalRead += numRead;
+
+					if (totalRead == buffer.Length)
+						return totalRead;
+				}
+				else
+				{
+                    Asn1OctetStringParser next = GetNextParser();
+                    if (next == null)
+					{
+						m_currentStream = null;
+						return totalRead;
+					}
+
+					m_currentStream = next.GetOctetStream();
+				}
+			}
+		}
+#endif
+
 		public override int ReadByte()
 		{
 			if (m_currentStream == null)
diff --git a/crypto/src/asn1/DefiniteLengthInputStream.cs b/crypto/src/asn1/DefiniteLengthInputStream.cs
index ed5bd2446..89f0d5a62 100644
--- a/crypto/src/asn1/DefiniteLengthInputStream.cs
+++ b/crypto/src/asn1/DefiniteLengthInputStream.cs
@@ -79,6 +79,27 @@ namespace Org.BouncyCastle.Asn1
             return numRead;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int Read(Span<byte> buffer)
+        {
+            if (_remaining == 0)
+                return 0;
+
+            int toRead = System.Math.Min(buffer.Length, _remaining);
+            int numRead = _in.Read(buffer[..toRead]);
+
+            if (numRead < 1)
+                throw new EndOfStreamException("DEF length " + _originalLength + " object truncated by " + _remaining);
+
+            if ((_remaining -= numRead) == 0)
+            {
+                SetParentEofDetect();
+            }
+
+            return numRead;
+        }
+#endif
+
         internal void ReadAllIntoByteArray(byte[] buf)
         {
             if (_remaining != buf.Length)
diff --git a/crypto/src/asn1/IndefiniteLengthInputStream.cs b/crypto/src/asn1/IndefiniteLengthInputStream.cs
index 1c8bd9a15..e192e9e8b 100644
--- a/crypto/src/asn1/IndefiniteLengthInputStream.cs
+++ b/crypto/src/asn1/IndefiniteLengthInputStream.cs
@@ -57,7 +57,28 @@ namespace Org.BouncyCastle.Asn1
 			return numRead + 1;
 		}
 
-		public override int ReadByte()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int Read(Span<byte> buffer)
+        {
+            // Only use this optimisation if we aren't checking for 00
+            if (_eofOn00 || buffer.Length <= 1)
+                return base.Read(buffer);
+
+            if (_lookAhead < 0)
+                return 0;
+
+            int numRead = _in.Read(buffer[1..]);
+            if (numRead <= 0)
+                throw new EndOfStreamException();
+
+            buffer[0] = (byte)_lookAhead;
+            _lookAhead = RequireByte();
+
+            return numRead + 1;
+        }
+#endif
+
+        public override int ReadByte()
 		{
             if (_eofOn00 && _lookAhead <= 0)
             {
diff --git a/crypto/src/bcpg/ArmoredInputStream.cs b/crypto/src/bcpg/ArmoredInputStream.cs
index 1c5ebd7c5..4fbb8baae 100644
--- a/crypto/src/bcpg/ArmoredInputStream.cs
+++ b/crypto/src/bcpg/ArmoredInputStream.cs
@@ -1,3 +1,4 @@
+using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Text;
@@ -330,6 +331,26 @@ namespace Org.BouncyCastle.Bcpg
             return pos;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int Read(Span<byte> buffer)
+        {
+            /*
+             * TODO Currently can't return partial data when exception thrown (breaking test case), so we don't inherit
+             * the base class implementation. Probably the reason is that throws don't mark this instance as 'failed'.
+             */
+            int pos = 0;
+            while (pos < buffer.Length)
+            {
+                int b = ReadByte();
+                if (b < 0)
+                    break;
+
+                buffer[pos++] = (byte)b;
+            }
+            return pos;
+        }
+#endif
+
         public override int ReadByte()
         {
             if (start)
diff --git a/crypto/src/bcpg/BcpgInputStream.cs b/crypto/src/bcpg/BcpgInputStream.cs
index 7a19a90dd..3b6f61bbc 100644
--- a/crypto/src/bcpg/BcpgInputStream.cs
+++ b/crypto/src/bcpg/BcpgInputStream.cs
@@ -57,6 +57,21 @@ namespace Org.BouncyCastle.Bcpg
             return 1;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int Read(Span<byte> buffer)
+        {
+			if (!next)
+				return m_in.Read(buffer);
+
+			if (nextB < 0)
+				return 0;
+
+            buffer[0] = (byte)nextB;
+            next = false;
+            return 1;
+        }
+#endif
+
         public byte[] ReadAll()
         {
 			return Streams.ReadAll(this);
@@ -312,9 +327,8 @@ namespace Org.BouncyCastle.Bcpg
 						int readLen = (dataLength > count || dataLength < 0) ? count : dataLength;
 						int len = m_in.Read(buffer, offset, readLen);
 						if (len < 1)
-						{
 							throw new EndOfStreamException("Premature end of stream in PartialInputStream");
-						}
+
 						dataLength -= len;
 						return len;
 					}
@@ -324,6 +338,29 @@ namespace Org.BouncyCastle.Bcpg
 				return 0;
 			}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public override int Read(Span<byte> buffer)
+            {
+				do
+				{
+					if (dataLength != 0)
+					{
+                        int count = buffer.Length;
+						int readLen = (dataLength > count || dataLength < 0) ? count : dataLength;
+						int len = m_in.Read(buffer[..readLen]);
+						if (len < 1)
+							throw new EndOfStreamException("Premature end of stream in PartialInputStream");
+
+						dataLength -= len;
+						return len;
+					}
+				}
+				while (partial && ReadPartialDataLength() >= 0);
+
+				return 0;
+            }
+#endif
+
             private int ReadPartialDataLength()
             {
                 int l = m_in.ReadByte();
diff --git a/crypto/src/bcpg/BcpgOutputStream.cs b/crypto/src/bcpg/BcpgOutputStream.cs
index fbdd75bff..690686d88 100644
--- a/crypto/src/bcpg/BcpgOutputStream.cs
+++ b/crypto/src/bcpg/BcpgOutputStream.cs
@@ -164,7 +164,7 @@ namespace Org.BouncyCastle.Bcpg
 
             if (partialBuffer != null)
             {
-                PartialFlush(true);
+                PartialFlushLast();
                 partialBuffer = null;
             }
 
@@ -215,19 +215,26 @@ namespace Org.BouncyCastle.Bcpg
             }
         }
 
-        private void PartialFlush(bool isLast)
+        private void PartialFlush()
         {
-            if (isLast)
-            {
-                WriteNewPacketLength(partialOffset);
-                outStr.Write(partialBuffer, 0, partialOffset);
-            }
-            else
-            {
-                outStr.WriteByte((byte)(0xE0 | partialPower));
-                outStr.Write(partialBuffer, 0, partialBufferLength);
-            }
+            outStr.WriteByte((byte)(0xE0 | partialPower));
+            outStr.Write(partialBuffer, 0, partialBufferLength);
+            partialOffset = 0;
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void PartialFlush(ref ReadOnlySpan<byte> buffer)
+        {
+            outStr.WriteByte((byte)(0xE0 | partialPower));
+            outStr.Write(buffer[..partialBufferLength]);
+            buffer = buffer[partialBufferLength..];
+        }
+#endif
 
+        private void PartialFlushLast()
+        {
+            WriteNewPacketLength(partialOffset);
+            outStr.Write(partialBuffer, 0, partialOffset);
             partialOffset = 0;
         }
 
@@ -235,40 +242,71 @@ namespace Org.BouncyCastle.Bcpg
         {
             Streams.ValidateBufferArguments(buffer, offset, count);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            PartialWrite(buffer.AsSpan(offset, count));
+#else
             if (partialOffset == partialBufferLength)
             {
-                PartialFlush(false);
+                PartialFlush();
             }
 
             if (count <= (partialBufferLength - partialOffset))
             {
                 Array.Copy(buffer, offset, partialBuffer, partialOffset, count);
                 partialOffset += count;
+                return;
             }
-            else
+
+            int diff = partialBufferLength - partialOffset;
+            Array.Copy(buffer, offset, partialBuffer, partialOffset, diff);
+            offset += diff;
+            count -= diff;
+            PartialFlush();
+            while (count > partialBufferLength)
             {
-                int diff = partialBufferLength - partialOffset;
-                Array.Copy(buffer, offset, partialBuffer, partialOffset, diff);
-                offset += diff;
-                count -= diff;
-                PartialFlush(false);
-                while (count > partialBufferLength)
-                {
-                    Array.Copy(buffer, offset, partialBuffer, 0, partialBufferLength);
-                    offset += partialBufferLength;
-                    count -= partialBufferLength;
-                    PartialFlush(false);
-                }
-                Array.Copy(buffer, offset, partialBuffer, 0, count);
-                partialOffset += count;
+                Array.Copy(buffer, offset, partialBuffer, 0, partialBufferLength);
+                offset += partialBufferLength;
+                count -= partialBufferLength;
+                PartialFlush();
             }
+            Array.Copy(buffer, offset, partialBuffer, 0, count);
+            partialOffset = count;
+#endif
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void PartialWrite(ReadOnlySpan<byte> buffer)
+        {
+            if (partialOffset == partialBufferLength)
+            {
+                PartialFlush();
+            }
+
+            if (buffer.Length <= (partialBufferLength - partialOffset))
+            {
+                buffer.CopyTo(partialBuffer.AsSpan(partialOffset));
+                partialOffset += buffer.Length;
+                return;
+            }
+
+            int diff = partialBufferLength - partialOffset;
+            buffer[..diff].CopyTo(partialBuffer.AsSpan(partialOffset));
+            buffer = buffer[diff..];
+            PartialFlush();
+            while (buffer.Length > partialBufferLength)
+            {
+                PartialFlush(ref buffer);
+            }
+            buffer.CopyTo(partialBuffer);
+            partialOffset = buffer.Length;
+        }
+#endif
+
         private void PartialWriteByte(byte value)
         {
             if (partialOffset == partialBufferLength)
             {
-                PartialFlush(false);
+                PartialFlush();
             }
 
             partialBuffer[partialOffset++] = value;
@@ -286,6 +324,20 @@ namespace Org.BouncyCastle.Bcpg
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void Write(ReadOnlySpan<byte> buffer)
+        {
+            if (partialBuffer != null)
+            {
+                PartialWrite(buffer);
+            }
+            else
+            {
+                outStr.Write(buffer);
+            }
+        }
+#endif
+
         public override void WriteByte(byte value)
         {
             if (partialBuffer != null)
@@ -370,7 +422,7 @@ namespace Org.BouncyCastle.Bcpg
         {
             if (partialBuffer != null)
             {
-                PartialFlush(true);
+                PartialFlushLast();
                 Array.Clear(partialBuffer, 0, partialBuffer.Length);
                 partialBuffer = null;
             }
diff --git a/crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs b/crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs
index b2c5cac28..6348431a2 100644
--- a/crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs
+++ b/crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs
@@ -237,7 +237,14 @@ namespace Org.BouncyCastle.Cms
                 macStream.Write(buffer, offset, count);
             }
 
-			public override void WriteByte(byte value)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public override void Write(ReadOnlySpan<byte> buffer)
+            {
+                macStream.Write(buffer);
+            }
+#endif
+
+            public override void WriteByte(byte value)
 			{
 				macStream.WriteByte(value);
 			}
diff --git a/crypto/src/cms/CMSCompressedDataStreamGenerator.cs b/crypto/src/cms/CMSCompressedDataStreamGenerator.cs
index 1594500cd..3669c0b3a 100644
--- a/crypto/src/cms/CMSCompressedDataStreamGenerator.cs
+++ b/crypto/src/cms/CMSCompressedDataStreamGenerator.cs
@@ -4,6 +4,7 @@ using System.IO;
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.Cms;
 using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto.IO;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.IO;
 
@@ -118,7 +119,14 @@ namespace Org.BouncyCastle.Cms
 				_out.Write(buffer, offset, count);
 			}
 
-			public override void WriteByte(byte value)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public override void Write(ReadOnlySpan<byte> buffer)
+			{
+                _out.Write(buffer);
+            }
+#endif
+
+            public override void WriteByte(byte value)
 			{
 				_out.WriteByte(value);
 			}
diff --git a/crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs b/crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs
index 6a362e13f..ad356a208 100644
--- a/crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs
+++ b/crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs
@@ -240,7 +240,14 @@ namespace Org.BouncyCastle.Cms
 				_out.Write(buffer, offset, count);
 			}
 
-			public override void WriteByte(byte value)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public override void Write(ReadOnlySpan<byte> buffer)
+            {
+                _out.Write(buffer);
+            }
+#endif
+
+            public override void WriteByte(byte value)
 			{
 				_out.WriteByte(value);
 			}
diff --git a/crypto/src/cms/CMSSignedDataStreamGenerator.cs b/crypto/src/cms/CMSSignedDataStreamGenerator.cs
index f934b9259..3f2da5f7e 100644
--- a/crypto/src/cms/CMSSignedDataStreamGenerator.cs
+++ b/crypto/src/cms/CMSSignedDataStreamGenerator.cs
@@ -783,7 +783,14 @@ namespace Org.BouncyCastle.Cms
 				_out.Write(buffer, offset, count);
 			}
 
-			public override void WriteByte(byte value)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public override void Write(ReadOnlySpan<byte> buffer)
+            {
+                _out.Write(buffer);
+            }
+#endif
+
+            public override void WriteByte(byte value)
 			{
 				_out.WriteByte(value);
 			}
diff --git a/crypto/src/cms/CMSTypedStream.cs b/crypto/src/cms/CMSTypedStream.cs
index d4ca62256..7a66f0c74 100644
--- a/crypto/src/cms/CMSTypedStream.cs
+++ b/crypto/src/cms/CMSTypedStream.cs
@@ -59,13 +59,6 @@ namespace Org.BouncyCastle.Cms
 			{
 			}
 
-#if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-            public override void CopyTo(Stream destination, int bufferSize)
-            {
-				s.CopyTo(destination, bufferSize);
-            }
-#endif
-
             public override int Read(byte[]	buf, int off, int len)
 			{
 				return Streams.ReadFully(s, buf, off, len);
diff --git a/crypto/src/openpgp/PgpEncryptedData.cs b/crypto/src/openpgp/PgpEncryptedData.cs
index 5cdc0d533..bad4cb8cd 100644
--- a/crypto/src/openpgp/PgpEncryptedData.cs
+++ b/crypto/src/openpgp/PgpEncryptedData.cs
@@ -54,6 +54,9 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             {
 				Streams.ValidateBufferArguments(buffer, offset, count);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                return Read(buffer.AsSpan(offset, count));
+#else
                 int avail = bufEnd - bufStart;
 
                 int pos = offset;
@@ -73,8 +76,34 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 bufStart += count;
 
                 return pos + count - offset;
+#endif
             }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public override int Read(Span<byte> buffer)
+            {
+                int avail = bufEnd - bufStart;
+
+                int pos = 0, count = buffer.Length;
+                while (count > avail)
+                {
+                    lookAhead.AsSpan(bufStart, avail).CopyTo(buffer[pos..]);
+
+                    bufStart += avail;
+                    pos += avail;
+                    count -= avail;
+
+                    if ((avail = FillBuffer()) < 1)
+                        return pos;
+                }
+
+                lookAhead.AsSpan(bufStart, count).CopyTo(buffer[pos..]);
+                bufStart += count;
+
+                return pos + count;
+            }
+#endif
+
             public override int ReadByte()
             {
                 if (bufStart < bufEnd)
diff --git a/crypto/src/openpgp/WrappedGeneratorStream.cs b/crypto/src/openpgp/WrappedGeneratorStream.cs
index c54ee0b3b..6f96dc9b8 100644
--- a/crypto/src/openpgp/WrappedGeneratorStream.cs
+++ b/crypto/src/openpgp/WrappedGeneratorStream.cs
@@ -13,10 +13,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 		internal WrappedGeneratorStream(IStreamGenerator generator, Stream s)
 			: base(s)
 		{
-			if (generator == null)
-				throw new ArgumentNullException(nameof(generator));
-
-			m_generator = generator;
+			m_generator = generator ?? throw new ArgumentNullException(nameof(generator));
 		}
 
         protected override void Dispose(bool disposing)
diff --git a/crypto/src/tls/CombinedHash.cs b/crypto/src/tls/CombinedHash.cs
index 71151d2a5..360b9d426 100644
--- a/crypto/src/tls/CombinedHash.cs
+++ b/crypto/src/tls/CombinedHash.cs
@@ -43,6 +43,14 @@ namespace Org.BouncyCastle.Tls
             m_sha1.Update(input, inOff, len);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void Update(ReadOnlySpan<byte> input)
+        {
+            m_md5.Update(input);
+            m_sha1.Update(input);
+        }
+#endif
+
         public virtual byte[] CalculateHash()
         {
             if (null != m_context && TlsUtilities.IsSsl(m_context))
diff --git a/crypto/src/tls/DeferredHash.cs b/crypto/src/tls/DeferredHash.cs
index 82f7899a5..e6397ab1e 100644
--- a/crypto/src/tls/DeferredHash.cs
+++ b/crypto/src/tls/DeferredHash.cs
@@ -176,6 +176,22 @@ namespace Org.BouncyCastle.Tls
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void Update(ReadOnlySpan<byte> input)
+        {
+            if (m_buf != null)
+            {
+                m_buf.Write(input);
+                return;
+            }
+
+            foreach (TlsHash hash in m_hashes.Values)
+            {
+                hash.Update(input);
+            }
+        }
+#endif
+
         public byte[] CalculateHash()
         {
             throw new InvalidOperationException("Use 'ForkPrfHash' to get a definite hash");
diff --git a/crypto/src/tls/crypto/TlsHash.cs b/crypto/src/tls/crypto/TlsHash.cs
index 4732fc280..6fbaeceb9 100644
--- a/crypto/src/tls/crypto/TlsHash.cs
+++ b/crypto/src/tls/crypto/TlsHash.cs
@@ -11,6 +11,10 @@ namespace Org.BouncyCastle.Tls.Crypto
         /// <param name="length">the length of the input data.</param>
         void Update(byte[] input, int inOff, int length);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        void Update(ReadOnlySpan<byte> input);
+#endif
+
         /// <summary>Return calculated hash for any input passed in.</summary>
         /// <returns>the hash value.</returns>
         byte[] CalculateHash();
diff --git a/crypto/src/tls/crypto/TlsHashSink.cs b/crypto/src/tls/crypto/TlsHashSink.cs
index a1681b0c8..3401eb60e 100644
--- a/crypto/src/tls/crypto/TlsHashSink.cs
+++ b/crypto/src/tls/crypto/TlsHashSink.cs
@@ -29,6 +29,16 @@ namespace Org.BouncyCastle.Tls.Crypto
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void Write(ReadOnlySpan<byte> buffer)
+        {
+            if (!buffer.IsEmpty)
+            {
+                m_hash.Update(buffer);
+            }
+        }
+#endif
+
         public override void WriteByte(byte value)
         {
             m_hash.Update(new byte[]{ value }, 0, 1);
diff --git a/crypto/src/tls/crypto/TlsMac.cs b/crypto/src/tls/crypto/TlsMac.cs
index a898a9bcc..511e29d10 100644
--- a/crypto/src/tls/crypto/TlsMac.cs
+++ b/crypto/src/tls/crypto/TlsMac.cs
@@ -21,6 +21,10 @@ namespace Org.BouncyCastle.Tls.Crypto
         /// <param name="length">the length of the input data.</param>
         void Update(byte[] input, int inOff, int length);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        void Update(ReadOnlySpan<byte> input);
+#endif
+
         /// <summary>Return calculated MAC for any input passed in.</summary>
         /// <returns>the MAC value.</returns>
         byte[] CalculateMac();
diff --git a/crypto/src/tls/crypto/TlsMacSink.cs b/crypto/src/tls/crypto/TlsMacSink.cs
index e7d5c70d7..fbb2e5893 100644
--- a/crypto/src/tls/crypto/TlsMacSink.cs
+++ b/crypto/src/tls/crypto/TlsMacSink.cs
@@ -29,6 +29,16 @@ namespace Org.BouncyCastle.Tls.Crypto
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void Write(ReadOnlySpan<byte> buffer)
+        {
+            if (!buffer.IsEmpty)
+            {
+                m_mac.Update(buffer);
+            }
+        }
+#endif
+
         public override void WriteByte(byte value)
         {
             m_mac.Update(new byte[]{ value }, 0, 1);
diff --git a/crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs b/crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs
index f26a50d46..a0378e334 100644
--- a/crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs
@@ -66,6 +66,13 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             m_digest.BlockUpdate(input, inOff, len);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void Update(ReadOnlySpan<byte> input)
+        {
+            m_digest.BlockUpdate(input);
+        }
+#endif
+
         public virtual byte[] CalculateMac()
         {
             byte[] result = new byte[m_digest.GetDigestSize()];
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsHash.cs b/crypto/src/tls/crypto/impl/bc/BcTlsHash.cs
index 0b35831f3..0ad2576cb 100644
--- a/crypto/src/tls/crypto/impl/bc/BcTlsHash.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsHash.cs
@@ -28,6 +28,13 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             m_digest.BlockUpdate(data, offSet, length);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void Update(ReadOnlySpan<byte> input)
+        {
+            m_digest.BlockUpdate(input);
+        }
+#endif
+
         public byte[] CalculateHash()
         {
             byte[] rv = new byte[m_digest.GetDigestSize()];
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs b/crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs
index 7a2318a31..dbe7f4c69 100644
--- a/crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs
@@ -32,6 +32,13 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             m_hmac.BlockUpdate(input, inOff, length);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void Update(ReadOnlySpan<byte> input)
+        {
+            m_hmac.BlockUpdate(input);
+        }
+#endif
+
         public byte[] CalculateMac()
         {
             byte[] rv = new byte[m_hmac.GetMacSize()];
diff --git a/crypto/src/util/io/BaseInputStream.cs b/crypto/src/util/io/BaseInputStream.cs
index ebe256632..eaaf9556d 100644
--- a/crypto/src/util/io/BaseInputStream.cs
+++ b/crypto/src/util/io/BaseInputStream.cs
@@ -45,5 +45,9 @@ namespace Org.BouncyCastle.Utilities.IO
         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
+        public override void Write(ReadOnlySpan<byte> buffer) { throw new NotSupportedException(); }
+#endif
     }
 }
diff --git a/crypto/src/util/io/BaseOutputStream.cs b/crypto/src/util/io/BaseOutputStream.cs
index dad9b19a4..0fc8e9681 100644
--- a/crypto/src/util/io/BaseOutputStream.cs
+++ b/crypto/src/util/io/BaseOutputStream.cs
@@ -21,6 +21,9 @@ namespace Org.BouncyCastle.Utilities.IO
             set { throw new NotSupportedException(); }
         }
         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(); }
+#endif
         public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
         public sealed override void SetLength(long value) { throw new NotSupportedException(); }
 
diff --git a/crypto/src/util/io/FilterStream.cs b/crypto/src/util/io/FilterStream.cs
index d9bcbb8ef..38077edd2 100644
--- a/crypto/src/util/io/FilterStream.cs
+++ b/crypto/src/util/io/FilterStream.cs
@@ -10,10 +10,7 @@ namespace Org.BouncyCastle.Utilities.IO
 
         public FilterStream(Stream s)
         {
-            if (s == null)
-                throw new ArgumentNullException(nameof(s));
-
-            this.s = s;
+            this.s = s ?? throw new ArgumentNullException(nameof(s));
         }
         public override bool CanRead
         {
@@ -27,6 +24,12 @@ namespace Org.BouncyCastle.Utilities.IO
         {
             get { return s.CanWrite; }
         }
+#if NETCOREAPP2_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void CopyTo(Stream destination, int bufferSize)
+        {
+            s.CopyTo(destination, bufferSize);
+        }
+#endif
         public override void Flush()
         {
             s.Flush();
@@ -44,6 +47,12 @@ namespace Org.BouncyCastle.Utilities.IO
         {
             return s.Read(buffer, offset, count);
         }
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int Read(Span<byte> buffer)
+        {
+            return s.Read(buffer);
+        }
+#endif
         public override int ReadByte()
         {
             return s.ReadByte();
@@ -60,6 +69,12 @@ namespace Org.BouncyCastle.Utilities.IO
         {
             s.Write(buffer, offset, count);
         }
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void Write(ReadOnlySpan<byte> buffer)
+        {
+            s.Write(buffer);
+        }
+#endif
         public override void WriteByte(byte value)
         {
             s.WriteByte(value);