summary refs log tree commit diff
path: root/crypto
diff options
context:
space:
mode:
authorDavid Hook <dgh@cryptoworkshop.com>2022-08-25 12:58:05 +1000
committerDavid Hook <dgh@cryptoworkshop.com>2022-08-25 12:58:05 +1000
commitfa5fc2339e292e53a8c1c9cc16c2e8242f5066d1 (patch)
tree5487957c1417f3ae0028dd62de3d2b26368135eb /crypto
parentinitial CRYSTALS-Kyber implementation (diff)
parentSpan-based variant for IMac.DoFinal (diff)
downloadBouncyCastle.NET-ed25519-fa5fc2339e292e53a8c1c9cc16c2e8242f5066d1.tar.xz
Merge remote-tracking branch 'refs/remotes/origin/master'
Diffstat (limited to 'crypto')
-rw-r--r--crypto/src/BouncyCastle.Crypto.csproj2
-rw-r--r--crypto/src/crypto/IBlockCipher.cs16
-rw-r--r--crypto/src/crypto/IDSA.cs39
-rw-r--r--crypto/src/crypto/IDigest.cs84
-rw-r--r--crypto/src/crypto/IDsaExt.cs17
-rw-r--r--crypto/src/crypto/IMac.cs84
-rw-r--r--crypto/src/crypto/ISigner.cs61
-rw-r--r--crypto/src/crypto/IXof.cs27
-rw-r--r--crypto/src/crypto/digests/Blake2bDigest.cs132
-rw-r--r--crypto/src/crypto/digests/Blake2sDigest.cs137
-rw-r--r--crypto/src/crypto/digests/CSHAKEDigest.cs19
-rw-r--r--crypto/src/crypto/digests/DSTU7564Digest.cs131
-rw-r--r--crypto/src/crypto/digests/GOST3411Digest.cs54
-rw-r--r--crypto/src/crypto/digests/GOST3411_2012Digest.cs92
-rw-r--r--crypto/src/crypto/digests/GOST3411_2012_256Digest.cs12
-rw-r--r--crypto/src/crypto/digests/GeneralDigest.cs50
-rw-r--r--crypto/src/crypto/digests/Haraka256Digest.cs90
-rw-r--r--crypto/src/crypto/digests/Haraka512Digest.cs122
-rw-r--r--crypto/src/crypto/digests/HarakaBase.cs12
-rw-r--r--crypto/src/crypto/digests/KeccakDigest.cs97
-rw-r--r--crypto/src/crypto/digests/LongDigest.cs289
-rw-r--r--crypto/src/crypto/digests/MD2Digest.cs59
-rw-r--r--crypto/src/crypto/digests/MD4Digest.cs53
-rw-r--r--crypto/src/crypto/digests/MD5Digest.cs32
-rw-r--r--crypto/src/crypto/digests/NonMemoableDigest.cs14
-rw-r--r--crypto/src/crypto/digests/NullDigest.cs35
-rw-r--r--crypto/src/crypto/digests/ParallelHash.cs116
-rw-r--r--crypto/src/crypto/digests/RipeMD128Digest.cs28
-rw-r--r--crypto/src/crypto/digests/RipeMD160Digest.cs29
-rw-r--r--crypto/src/crypto/digests/RipeMD256Digest.cs32
-rw-r--r--crypto/src/crypto/digests/RipeMD320Digest.cs34
-rw-r--r--crypto/src/crypto/digests/SHA3Digest.cs9
-rw-r--r--crypto/src/crypto/digests/SM3Digest.cs34
-rw-r--r--crypto/src/crypto/digests/Sha1Digest.cs34
-rw-r--r--crypto/src/crypto/digests/Sha224Digest.cs44
-rw-r--r--crypto/src/crypto/digests/Sha256Digest.cs59
-rw-r--r--crypto/src/crypto/digests/Sha384Digest.cs25
-rw-r--r--crypto/src/crypto/digests/Sha512Digest.cs21
-rw-r--r--crypto/src/crypto/digests/Sha512tDigest.cs47
-rw-r--r--crypto/src/crypto/digests/ShakeDigest.cs28
-rw-r--r--crypto/src/crypto/digests/ShortenedDigest.cs25
-rw-r--r--crypto/src/crypto/digests/SkeinDigest.cs18
-rw-r--r--crypto/src/crypto/digests/SkeinEngine.cs105
-rw-r--r--crypto/src/crypto/digests/TigerDigest.cs70
-rw-r--r--crypto/src/crypto/digests/TupleHash.cs53
-rw-r--r--crypto/src/crypto/digests/WhirlpoolDigest.cs24
-rw-r--r--crypto/src/crypto/digests/XofUtils.cs50
-rw-r--r--crypto/src/crypto/engines/AesEngine.cs131
-rw-r--r--crypto/src/crypto/engines/AesLightEngine.cs131
-rw-r--r--crypto/src/crypto/engines/AriaEngine.cs30
-rw-r--r--crypto/src/crypto/engines/BlowfishEngine.cs107
-rw-r--r--crypto/src/crypto/engines/CamelliaEngine.cs214
-rw-r--r--crypto/src/crypto/engines/CamelliaLightEngine.cs214
-rw-r--r--crypto/src/crypto/engines/Cast5Engine.cs197
-rw-r--r--crypto/src/crypto/engines/Cast6Engine.cs70
-rw-r--r--crypto/src/crypto/engines/DesEdeEngine.cs57
-rw-r--r--crypto/src/crypto/engines/DesEngine.cs59
-rw-r--r--crypto/src/crypto/engines/Dstu7624Engine.cs216
-rw-r--r--crypto/src/crypto/engines/GOST28147Engine.cs144
-rw-r--r--crypto/src/crypto/engines/IdeaEngine.cs122
-rw-r--r--crypto/src/crypto/engines/NoekeonEngine.cs177
-rw-r--r--crypto/src/crypto/engines/NullEngine.cs23
-rw-r--r--crypto/src/crypto/engines/RC2Engine.cs187
-rw-r--r--crypto/src/crypto/engines/RC532Engine.cs183
-rw-r--r--crypto/src/crypto/engines/RC564Engine.cs183
-rw-r--r--crypto/src/crypto/engines/RC6Engine.cs297
-rw-r--r--crypto/src/crypto/engines/RijndaelEngine.cs91
-rw-r--r--crypto/src/crypto/engines/SEEDEngine.cs96
-rw-r--r--crypto/src/crypto/engines/SM4Engine.cs31
-rw-r--r--crypto/src/crypto/engines/SerpentEngine.cs141
-rw-r--r--crypto/src/crypto/engines/SerpentEngineBase.cs39
-rw-r--r--crypto/src/crypto/engines/SkipjackEngine.cs182
-rw-r--r--crypto/src/crypto/engines/TEAEngine.cs108
-rw-r--r--crypto/src/crypto/engines/ThreefishEngine.cs32
-rw-r--r--crypto/src/crypto/engines/TnepresEngine.cs141
-rw-r--r--crypto/src/crypto/engines/TwofishEngine.cs128
-rw-r--r--crypto/src/crypto/engines/XTEAEngine.cs84
-rw-r--r--crypto/src/crypto/macs/CMac.cs82
-rw-r--r--crypto/src/crypto/macs/CbcBlockCipherMac.cs89
-rw-r--r--crypto/src/crypto/macs/CfbBlockCipherMac.cs154
-rw-r--r--crypto/src/crypto/macs/DSTU7564Mac.cs35
-rw-r--r--crypto/src/crypto/macs/DSTU7624Mac.cs220
-rw-r--r--crypto/src/crypto/macs/GMac.cs22
-rw-r--r--crypto/src/crypto/macs/GOST28147Mac.cs266
-rw-r--r--crypto/src/crypto/macs/HMac.cs45
-rw-r--r--crypto/src/crypto/macs/ISO9797Alg3Mac.cs90
-rw-r--r--crypto/src/crypto/macs/KMac.cs71
-rw-r--r--crypto/src/crypto/macs/Poly1305.cs139
-rw-r--r--crypto/src/crypto/macs/SipHash.cs55
-rw-r--r--crypto/src/crypto/macs/SkeinMac.cs15
-rw-r--r--crypto/src/crypto/macs/VMPCMac.cs73
-rw-r--r--crypto/src/crypto/modes/CbcBlockCipher.cs149
-rw-r--r--crypto/src/crypto/modes/CcmBlockCipher.cs10
-rw-r--r--crypto/src/crypto/modes/CfbBlockCipher.cs136
-rw-r--r--crypto/src/crypto/modes/ChaCha20Poly1305.cs13
-rw-r--r--crypto/src/crypto/modes/EAXBlockCipher.cs15
-rw-r--r--crypto/src/crypto/modes/GCMBlockCipher.cs46
-rw-r--r--crypto/src/crypto/modes/GOFBBlockCipher.cs107
-rw-r--r--crypto/src/crypto/modes/GcmSivBlockCipher.cs85
-rw-r--r--crypto/src/crypto/modes/IAeadCipher.cs7
-rw-r--r--crypto/src/crypto/modes/KCcmBlockCipher.cs7
-rw-r--r--crypto/src/crypto/modes/KCtrBlockCipher.cs55
-rw-r--r--crypto/src/crypto/modes/OCBBlockCipher.cs14
-rw-r--r--crypto/src/crypto/modes/OfbBlockCipher.cs58
-rw-r--r--crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs219
-rw-r--r--crypto/src/crypto/modes/SicBlockCipher.cs29
-rw-r--r--crypto/src/crypto/modes/gcm/GcmUtilities.cs15
-rw-r--r--crypto/src/crypto/signers/DsaDigestSigner.cs58
-rw-r--r--crypto/src/crypto/signers/DsaSigner.cs2
-rw-r--r--crypto/src/crypto/signers/ECDsaSigner.cs3
-rw-r--r--crypto/src/crypto/signers/ECGOST3410Signer.cs2
-rw-r--r--crypto/src/crypto/signers/ECNRSigner.cs2
-rw-r--r--crypto/src/crypto/signers/Ed25519Signer.cs7
-rw-r--r--crypto/src/crypto/signers/Ed25519ctxSigner.cs7
-rw-r--r--crypto/src/crypto/signers/Ed25519phSigner.cs7
-rw-r--r--crypto/src/crypto/signers/Ed448Signer.cs7
-rw-r--r--crypto/src/crypto/signers/Ed448phSigner.cs7
-rw-r--r--crypto/src/crypto/signers/GOST3410DigestSigner.cs42
-rw-r--r--crypto/src/crypto/signers/GOST3410Signer.cs2
-rw-r--r--crypto/src/crypto/signers/GenericSigner.cs25
-rw-r--r--crypto/src/crypto/signers/Iso9796d2PssSigner.cs37
-rw-r--r--crypto/src/crypto/signers/Iso9796d2Signer.cs46
-rw-r--r--crypto/src/crypto/signers/PssSigner.cs29
-rw-r--r--crypto/src/crypto/signers/RsaDigestSigner.cs34
-rw-r--r--crypto/src/crypto/signers/SM2Signer.cs11
-rw-r--r--crypto/src/crypto/signers/X931Signer.cs28
-rw-r--r--crypto/src/crypto/util/Pack.cs17
-rw-r--r--crypto/src/pqc/crypto/lms/LMSContext.cs14
-rw-r--r--crypto/src/pqc/crypto/sphincsplus/Adrs.cs3
-rw-r--r--crypto/src/pqc/crypto/sphincsplus/HarakaS256Digest.cs6
-rw-r--r--crypto/src/pqc/crypto/sphincsplus/HarakaS512Digest.cs7
-rw-r--r--crypto/src/pqc/crypto/sphincsplus/HarakaSBase.cs26
-rw-r--r--crypto/src/pqc/crypto/sphincsplus/HarakaSXof.cs8
-rw-r--r--crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs14
-rw-r--r--crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusKeyPairGenerator.cs10
-rw-r--r--crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusSigner.cs4
-rw-r--r--crypto/src/security/DigestUtilities.cs31
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs4
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs40
-rw-r--r--crypto/src/util/Integers.cs17
-rw-r--r--crypto/src/util/Longs.cs17
-rw-r--r--crypto/test/BouncyCastle.Crypto.Tests.csproj2
-rw-r--r--crypto/test/src/crypto/prng/test/CtrDrbgTest.cs9
-rw-r--r--crypto/test/src/crypto/test/Blake2bDigestTest.cs2
-rw-r--r--crypto/test/src/crypto/test/Blake2sDigestTest.cs2
-rw-r--r--crypto/test/src/crypto/test/CSHAKETest.cs3
-rw-r--r--crypto/test/src/crypto/test/DigestTest.cs59
-rw-r--r--crypto/test/src/crypto/test/GcmSivTest.cs9
-rw-r--r--crypto/test/src/crypto/test/Haraka256DigestTest.cs22
-rw-r--r--crypto/test/src/crypto/test/Haraka512DigestTest.cs21
-rw-r--r--crypto/test/src/crypto/test/KeccakDigestTest.cs2
-rw-r--r--crypto/test/src/crypto/test/ParallelHashTest.cs3
-rw-r--r--crypto/test/src/crypto/test/SHA3DigestTest.cs2
-rw-r--r--crypto/test/src/crypto/test/ShakeDigestTest.cs2
-rw-r--r--crypto/test/src/crypto/test/ShortenedDigestTest.cs2
-rw-r--r--crypto/test/src/crypto/test/SkeinDigestTest.cs2
-rw-r--r--crypto/test/src/crypto/test/TupleHashTest.cs62
157 files changed, 7193 insertions, 2366 deletions
diff --git a/crypto/src/BouncyCastle.Crypto.csproj b/crypto/src/BouncyCastle.Crypto.csproj
index bc17aae6a..2ebe4efdb 100644
--- a/crypto/src/BouncyCastle.Crypto.csproj
+++ b/crypto/src/BouncyCastle.Crypto.csproj
@@ -25,7 +25,7 @@
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
     </PackageReference>
-    <PackageReference Include="Nerdbank.GitVersioning" Version="3.5.108">
+    <PackageReference Include="Nerdbank.GitVersioning" Version="3.5.109">
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
     </PackageReference>
diff --git a/crypto/src/crypto/IBlockCipher.cs b/crypto/src/crypto/IBlockCipher.cs
index a3ad6d6e5..b26aaa49f 100644
--- a/crypto/src/crypto/IBlockCipher.cs
+++ b/crypto/src/crypto/IBlockCipher.cs
@@ -28,9 +28,19 @@ namespace Org.BouncyCastle.Crypto
 		/// <returns>The number of bytes processed and produced.</returns>
 		int ProcessBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff);
 
-		/// <summary>
-		/// Reset the cipher to the same state as it was after the last init (if there was one).
-		/// </summary>
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+
+		/// <summary>Process a block.</summary>
+		/// <param name="input">The input block as a span.</param>
+		/// <param name="output">The output span.</param>
+		/// <exception cref="DataLengthException">If input block is wrong size, or output span too small.</exception>
+		/// <returns>The number of bytes processed and produced.</returns>
+		int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output);
+#endif
+
+        /// <summary>
+        /// Reset the cipher to the same state as it was after the last init (if there was one).
+        /// </summary>
         void Reset();
     }
 }
diff --git a/crypto/src/crypto/IDSA.cs b/crypto/src/crypto/IDSA.cs
index 7d1fd5197..459914f1f 100644
--- a/crypto/src/crypto/IDSA.cs
+++ b/crypto/src/crypto/IDSA.cs
@@ -4,38 +4,29 @@ using Org.BouncyCastle.Math;
 
 namespace Org.BouncyCastle.Crypto
 {
-    /**
-     * interface for classes implementing the Digital Signature Algorithm
-     */
+    /// <summary>Interface for classes implementing the Digital Signature Algorithm</summary>
     public interface IDsa
     {
+        /// <summary>The algorithm name.</summary>
 		string AlgorithmName { get; }
 
-		/**
-         * initialise the signer for signature generation or signature
-         * verification.
-         *
-         * @param forSigning true if we are generating a signature, false
-         * otherwise.
-         * @param param key parameters for signature generation.
-         */
+        /// <summary>Initialise the signer for signature generation or signature verification.</summary>
+        /// <param name="forSigning">true if we are generating a signature, false otherwise.</param>
+        /// <param name="parameters">key parameters for signature generation.</param>
         void Init(bool forSigning, ICipherParameters parameters);
 
-        /**
-         * sign the passed in message (usually the output of a hash function).
-         *
-         * @param message the message to be signed.
-         * @return two big integers representing the r and s values respectively.
-         */
+        /// <summary>Sign the passed in message (usually the output of a hash function).</summary>
+        /// <param name="message">the message to be signed.</param>
+        /// <returns>two big integers representing the r and s values respectively.</returns>
         BigInteger[] GenerateSignature(byte[] message);
 
-        /**
-         * verify the message message against the signature values r and s.
-         *
-         * @param message the message that was supposed to have been signed.
-         * @param r the r signature value.
-         * @param s the s signature value.
-         */
+        /// <summary>The order of the group that the r, s values in signatures belong to.</summary>
+        BigInteger Order { get; }
+
+        /// <summary>Verify the message message against the signature values r and s.</summary>
+        /// <param name="message">the message that was supposed to have been signed.</param>
+        /// <param name="r">the r signature value.</param>
+        /// <param name="s">the s signature value.</param>
         bool VerifySignature(byte[] message, BigInteger  r, BigInteger s);
     }
 }
diff --git a/crypto/src/crypto/IDigest.cs b/crypto/src/crypto/IDigest.cs
index 6769dcc42..36caf3728 100644
--- a/crypto/src/crypto/IDigest.cs
+++ b/crypto/src/crypto/IDigest.cs
@@ -2,60 +2,52 @@ using System;
 
 namespace Org.BouncyCastle.Crypto
 {
-    /**
-     * interface that a message digest conforms to.
-     */
+    /// <remarks>Base interface for a message digest.</remarks>
     public interface IDigest
     {
-        /**
-         * return the algorithm name
-         *
-         * @return the algorithm name
-         */
+        /// <summary>The algorithm name.</summary>
         string AlgorithmName { get; }
 
-		/**
-         * return the size, in bytes, of the digest produced by this message digest.
-         *
-         * @return the size, in bytes, of the digest produced by this message digest.
-         */
-		int GetDigestSize();
-
-		/**
-         * return the size, in bytes, of the internal buffer used by this digest.
-         *
-         * @return the size, in bytes, of the internal buffer used by this digest.
-         */
-		int GetByteLength();
-
-		/**
-         * update the message digest with a single byte.
-         *
-         * @param inByte the input byte to be entered.
-         */
+        /// <summary>Return the size, in bytes, of the digest produced by this message digest.</summary>
+        /// <returns>the size, in bytes, of the digest produced by this message digest.</returns>
+        int GetDigestSize();
+
+        /// <summary>Return the size, in bytes, of the internal buffer used by this digest.</summary>
+        /// <returns>the size, in bytes, of the internal buffer used by this digest.</returns>
+        int GetByteLength();
+
+        /// <summary>Update the message digest with a single byte.</summary>
+        /// <param name="input">the input byte to be entered.</param>
         void Update(byte input);
 
-        /**
-         * update the message digest with a block of bytes.
-         *
-         * @param input the byte array containing the data.
-         * @param inOff the offset into the byte array where the data starts.
-         * @param len the length of the data.
-         */
-        void BlockUpdate(byte[] input, int inOff, int length);
-
-        /**
-         * Close the digest, producing the final digest value. The doFinal
-         * call leaves the digest reset.
-         *
-         * @param output the array the digest is to be copied into.
-         * @param outOff the offset into the out array the digest is to start at.
-         */
+        /// <summary>Update the message digest with a block of bytes.</summary>
+        /// <param name="input">the byte array containing the data.</param>
+        /// <param name="inOff">the offset into the byte array where the data starts.</param>
+        /// <param name="inLen">the length of the data.</param>
+        void BlockUpdate(byte[] input, int inOff, int inLen);
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        /// <summary>Update the message digest with a span of bytes.</summary>
+        /// <param name="input">the span containing the data.</param>
+        void BlockUpdate(ReadOnlySpan<byte> input);
+#endif
+
+        /// <summary>Close the digest, producing the final digest value.</summary>
+        /// <remarks>This call leaves the digest reset.</remarks>
+        /// <param name="output">the byte array the digest is to be copied into.</param>
+        /// <param name="outOff">the offset into the byte array the digest is to start at.</param>
+        /// <returns>the number of bytes written</returns>
         int DoFinal(byte[] output, int outOff);
 
-        /**
-         * reset the digest back to it's initial state.
-         */
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        /// <summary>Close the digest, producing the final digest value.</summary>
+        /// <remarks>This call leaves the digest reset.</remarks>
+        /// <param name="output">the span the digest is to be copied into.</param>
+        /// <returns>the number of bytes written</returns>
+        int DoFinal(Span<byte> output);
+#endif
+
+        /// <summary>Reset the digest back to its initial state.</summary>
         void Reset();
     }
 }
diff --git a/crypto/src/crypto/IDsaExt.cs b/crypto/src/crypto/IDsaExt.cs
deleted file mode 100644
index 15b55787a..000000000
--- a/crypto/src/crypto/IDsaExt.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-
-using Org.BouncyCastle.Math;
-
-namespace Org.BouncyCastle.Crypto
-{
-    /// <summary>
-    /// An "extended" interface for classes implementing DSA-style algorithms, that provides access
-    /// to the group order.
-    /// </summary>
-    public interface IDsaExt
-        : IDsa
-    {
-        /// <summary>The order of the group that the r, s values in signatures belong to.</summary>
-        BigInteger Order { get; }
-    }
-}
diff --git a/crypto/src/crypto/IMac.cs b/crypto/src/crypto/IMac.cs
index 03a86e8b6..aa539b968 100644
--- a/crypto/src/crypto/IMac.cs
+++ b/crypto/src/crypto/IMac.cs
@@ -2,68 +2,52 @@ using System;
 
 namespace Org.BouncyCastle.Crypto
 {
-    /**
-     * The base interface for implementations of message authentication codes (MACs).
-     */
+    /// <summary>The base interface for implementations of message authentication codes (MACs).</summary>
     public interface IMac
     {
-        /**
-         * Initialise the MAC.
-         *
-         * @param param the key and other data required by the MAC.
-         * @exception ArgumentException if the parameters argument is
-         * inappropriate.
-         */
+        /// <summary>Initialise the MAC.</summary>
+        /// <param name="parameters">The key or other data required by the MAC.</param>
         void Init(ICipherParameters parameters);
 
-        /**
-         * Return the name of the algorithm the MAC implements.
-         *
-         * @return the name of the algorithm the MAC implements.
-         */
+        /// <summary>The algorithm name.</summary>
         string AlgorithmName { get; }
 
-		/**
-		 * Return the block size for this MAC (in bytes).
-		 *
-		 * @return the block size for this MAC in bytes.
-		 */
-		int GetMacSize();
+        /// <summary>Return the size, in bytes, of the MAC produced by this implementation.</summary>
+        /// <returns>the size, in bytes, of the MAC produced by this implementation.</returns>
+        int GetMacSize();
 
-        /**
-         * add a single byte to the mac for processing.
-         *
-         * @param in the byte to be processed.
-         * @exception InvalidOperationException if the MAC is not initialised.
-         */
+        /// <summary>Update the MAC with a single byte.</summary>
+        /// <param name="input">the input byte to be entered.</param>
         void Update(byte input);
 
-		/**
-         * @param in the array containing the input.
-         * @param inOff the index in the array the data begins at.
-         * @param len the length of the input starting at inOff.
-         * @exception InvalidOperationException if the MAC is not initialised.
-         * @exception DataLengthException if there isn't enough data in in.
-         */
-        void BlockUpdate(byte[] input, int inOff, int len);
+        /// <summary>Update the MAC with a block of bytes.</summary>
+        /// <param name="input">the byte array containing the data.</param>
+        /// <param name="inOff">the offset into the byte array where the data starts.</param>
+        /// <param name="inLen">the length of the data.</param>
+        void BlockUpdate(byte[] input, int inOff, int inLen);
 
-		/**
-         * Compute the final stage of the MAC writing the output to the out
-         * parameter.
-         * <p>
-         * doFinal leaves the MAC in the same state it was after the last init.
-         * </p>
-         * @param out the array the MAC is to be output to.
-         * @param outOff the offset into the out buffer the output is to start at.
-         * @exception DataLengthException if there isn't enough space in out.
-         * @exception InvalidOperationException if the MAC is not initialised.
-         */
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        /// <summary>Update the MAC with a span of bytes.</summary>
+        /// <param name="input">the span containing the data.</param>
+        void BlockUpdate(ReadOnlySpan<byte> input);
+#endif
+
+        /// <summary>Perform final calculations, producing the result MAC.</summary>
+        /// <remarks>This call leaves the MAC reset.</remarks>
+        /// <param name="output">the byte array the MAC is to be copied into.</param>
+        /// <param name="outOff">the offset into the byte array the MAC is to start at.</param>
+        /// <returns>the number of bytes written</returns>
         int DoFinal(byte[] output, int outOff);
 
-		/**
-         * Reset the MAC. At the end of resetting the MAC should be in the
-         * in the same state it was after the last init (if there was one).
-         */
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        /// <summary>Perform final calculations, producing the result MAC.</summary>
+        /// <remarks>This call leaves the MAC reset.</remarks>
+        /// <param name="output">the span the MAC is to be copied into.</param>
+        /// <returns>the number of bytes written</returns>
+        int DoFinal(Span<byte> output);
+#endif
+
+        /// <summary>Reset the MAC back to its initial state.</summary>
         void Reset();
     }
 }
diff --git a/crypto/src/crypto/ISigner.cs b/crypto/src/crypto/ISigner.cs
index e03bbf4d3..668e5e4cd 100644
--- a/crypto/src/crypto/ISigner.cs
+++ b/crypto/src/crypto/ISigner.cs
@@ -1,50 +1,45 @@
-
 using System;
-using System.Text;
 
 namespace Org.BouncyCastle.Crypto
 {
     public interface ISigner
     {
-        /**
-         * Return the name of the algorithm the signer implements.
-         *
-         * @return the name of the algorithm the signer implements.
-         */
+        /// <summary>The algorithm name.</summary>
         string AlgorithmName { get; }
 
-		/**
-         * Initialise the signer for signing or verification.
-         *
-         * @param forSigning true if for signing, false otherwise
-         * @param param necessary parameters.
-         */
-         void Init(bool forSigning, ICipherParameters parameters);
-
-        /**
-         * update the internal digest with the byte b
-         */
+        /// <summary>Initialise the signer for signing or verification.</summary>
+        /// <param name="forSigning">true if for signing, false otherwise.</param>
+        /// <param name="parameters">necessary parameters.</param>
+        void Init(bool forSigning, ICipherParameters parameters);
+
+        /// <summary>Update the signer with a single byte.</summary>
+        /// <param name="input">the input byte to be entered.</param>
         void Update(byte input);
 
-        /**
-         * update the internal digest with the byte array in
-         */
-        void BlockUpdate(byte[] input, int inOff, int length);
+        /// <summary>Update the signer with a block of bytes.</summary>
+        /// <param name="input">the byte array containing the data.</param>
+        /// <param name="inOff">the offset into the byte array where the data starts.</param>
+        /// <param name="inLen">the length of the data.</param>
+        void BlockUpdate(byte[] input, int inOff, int inLen);
 
-        /**
-         * Generate a signature for the message we've been loaded with using
-         * the key we were initialised with.
-         */
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        /// <summary>Update the signer with a span of bytes.</summary>
+        /// <param name="input">the span containing the data.</param>
+        void BlockUpdate(ReadOnlySpan<byte> input);
+#endif
+
+        /// <summary>Generate a signature for the message we've been loaded with using the key we were initialised with.
+        /// </summary>
+        /// <returns>A byte array containing the signature for the message.</returns>
         byte[] GenerateSignature();
-        /**
-         * return true if the internal state represents the signature described
-         * in the passed in array.
-         */
+
+        /// <summary>Return true if the internal state represents the signature described in the passed in array.
+        /// </summary>
+        /// <param name="signature">an array containing the candidate signature to verify.</param>
+        /// <returns>true if the internal state represents the signature described in the passed in array.</returns>
         bool VerifySignature(byte[] signature);
 
-        /**
-         * reset the internal state
-         */
+        /// <summary>Reset the signer back to its initial state.</summary>
         void Reset();
     }
 }
diff --git a/crypto/src/crypto/IXof.cs b/crypto/src/crypto/IXof.cs
index f76304d48..8cddb2870 100644
--- a/crypto/src/crypto/IXof.cs
+++ b/crypto/src/crypto/IXof.cs
@@ -4,13 +4,13 @@ namespace Org.BouncyCastle.Crypto
 {
     /// <remarks>
     /// With FIPS PUB 202 a new kind of message digest was announced which supported extendable output, or variable digest sizes.
-    /// This interface provides the extra method required to support variable output on a digest implementation.
+    /// This interface provides the extra methods required to support variable output on a digest implementation.
     /// </remarks>
     public interface IXof
         : IDigest
     {
         /// <summary>
-        /// Output the results of the final calculation for this digest to outLen number of bytes.
+        /// Output the results of the final calculation for this XOF to outLen number of bytes.
         /// </summary>
         /// <param name="output">output array to write the output bytes to.</param>
         /// <param name="outOff">offset to start writing the bytes at.</param>
@@ -18,14 +18,33 @@ namespace Org.BouncyCastle.Crypto
         /// <returns>the number of bytes written</returns>
         int DoFinal(byte[] output, int outOff, int outLen);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         /// <summary>
-        /// Start outputting the results of the final calculation for this digest. Unlike DoFinal, this method
-        /// will continue producing output until the Xof is explicitly reset, or signals otherwise.
+        /// Output the results of the final calculation for this XOF to fill the output span.
+        /// </summary>
+        /// <param name="output">span to fill with the output bytes.</param>
+        /// <returns>the number of bytes written</returns>
+        int OutputFinal(Span<byte> output);
+#endif
+
+        /// <summary>
+        /// Start outputting the results of the final calculation for this XOF. Unlike DoFinal, this method
+        /// will continue producing output until the XOF is explicitly reset, or signals otherwise.
         /// </summary>
         /// <param name="output">output array to write the output bytes to.</param>
         /// <param name="outOff">offset to start writing the bytes at.</param>
         /// <param name="outLen">the number of output bytes requested.</param>
         /// <returns>the number of bytes written</returns>
         int DoOutput(byte[] output, int outOff, int outLen);
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        /// <summary>
+        /// Start outputting the results of the final calculation for this XOF. Unlike OutputFinal, this method
+        /// will continue producing output until the XOF is explicitly reset, or signals otherwise.
+        /// </summary>
+        /// <param name="output">span to fill with the output bytes.</param>
+        /// <returns>the number of bytes written</returns>
+        int Output(Span<byte> output);
+#endif
     }
 }
diff --git a/crypto/src/crypto/digests/Blake2bDigest.cs b/crypto/src/crypto/digests/Blake2bDigest.cs
index 1ac9cfa35..ec80a3355 100644
--- a/crypto/src/crypto/digests/Blake2bDigest.cs
+++ b/crypto/src/crypto/digests/Blake2bDigest.cs
@@ -357,11 +357,63 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
 
             // fill the buffer with left bytes, this might be a full block
-            Array.Copy(message, messagePos, buffer, 0, offset + len
-                - messagePos);
+            Array.Copy(message, messagePos, buffer, 0, offset + len - messagePos);
             bufferPos += offset + len - messagePos;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            if (input.IsEmpty)
+                return;
+
+            int remainingLength = 0; // left bytes of buffer
+
+            if (bufferPos != 0)
+            { // commenced, incomplete buffer
+
+                // complete the buffer:
+                remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
+                if (remainingLength < input.Length)
+                { // full buffer + at least 1 byte
+                    input[..remainingLength].CopyTo(buffer.AsSpan(bufferPos));
+                    t0 += BLOCK_LENGTH_BYTES;
+                    if (t0 == 0)
+                    { // if message > 2^64
+                        t1++;
+                    }
+                    Compress(buffer, 0);
+                    bufferPos = 0;
+                    Array.Clear(buffer, 0, buffer.Length);// clear buffer
+                }
+                else
+                {
+                    input.CopyTo(buffer.AsSpan(bufferPos));
+                    bufferPos += input.Length;
+                    return;
+                }
+            }
+
+            // process blocks except last block (also if last block is full)
+            int messagePos;
+            int blockWiseLastPos = input.Length - BLOCK_LENGTH_BYTES;
+            for (messagePos = remainingLength; messagePos < blockWiseLastPos; messagePos += BLOCK_LENGTH_BYTES)
+            { // block wise 128 bytes
+                // without buffer:
+                t0 += BLOCK_LENGTH_BYTES;
+                if (t0 == 0)
+                {
+                    t1++;
+                }
+                Compress(input[messagePos..]);
+            }
+
+            // fill the buffer with left bytes, this might be a full block
+            input[messagePos..].CopyTo(buffer.AsSpan());
+            bufferPos += input.Length - messagePos;
+        }
+#endif
+
         /**
          * close the digest, producing the final digest value. The doFinal
          * call leaves the digest reset.
@@ -382,19 +434,42 @@ namespace Org.BouncyCastle.Crypto.Digests
             Array.Clear(buffer, 0, buffer.Length);// Holds eventually the key if input is null
             Array.Clear(internalState, 0, internalState.Length);
 
-            byte[] bytes = new byte[8];
-            for (int i = 0; i < chainValue.Length && (i * 8 < digestLength); i++)
+            int full = digestLength >> 3, partial = digestLength & 7;
+            Pack.UInt64_To_LE(chainValue, 0, full, output, outOffset);
+            if (partial > 0)
+            {
+                byte[] bytes = new byte[8];
+                Pack.UInt64_To_LE(chainValue[full], bytes, 0);
+                Array.Copy(bytes, 0, output, outOffset + digestLength - partial, partial);
+            }
+
+            Array.Clear(chainValue, 0, chainValue.Length);
+
+            Reset();
+
+            return digestLength;
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int DoFinal(Span<byte> output)
+        {
+            f0 = 0xFFFFFFFFFFFFFFFFUL;
+            t0 += (ulong)bufferPos;
+            if (bufferPos > 0 && t0 == 0)
             {
-                Pack.UInt64_To_LE(chainValue[i], bytes, 0);
+                t1++;
+            }
+            Compress(buffer, 0);
+            Array.Clear(buffer, 0, buffer.Length);// Holds eventually the key if input is null
+            Array.Clear(internalState, 0, internalState.Length);
 
-                if (i * 8 < digestLength - 8)
-                {
-                    Array.Copy(bytes, 0, output, outOffset + i * 8, 8);
-                }
-                else
-                {
-                    Array.Copy(bytes, 0, output, outOffset + i * 8, digestLength - (i * 8));
-                }
+            int full = digestLength >> 3, partial = digestLength & 7;
+            Pack.UInt64_To_LE(chainValue.AsSpan(0, full), output);
+            if (partial > 0)
+            {
+                Span<byte> bytes = stackalloc byte[8];
+                Pack.UInt64_To_LE(chainValue[full], bytes);
+                bytes[..partial].CopyTo(output[(digestLength - partial)..]);
             }
 
             Array.Clear(chainValue, 0, chainValue.Length);
@@ -403,6 +478,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 
             return digestLength;
         }
+#endif
 
         /**
          * Reset the digest back to it's initial state.
@@ -453,6 +529,36 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void Compress(ReadOnlySpan<byte> message)
+        {
+            InitializeInternalState();
+
+            Span<ulong> m = stackalloc ulong[16];
+            Pack.LE_To_UInt64(message, m);
+
+            for (int round = 0; round < ROUNDS; round++)
+            {
+                // G apply to columns of internalState:m[blake2b_sigma[round][2 * blockPos]] /+1
+                G(m[blake2b_sigma[round, 0]], m[blake2b_sigma[round, 1]], 0, 4, 8, 12);
+                G(m[blake2b_sigma[round, 2]], m[blake2b_sigma[round, 3]], 1, 5, 9, 13);
+                G(m[blake2b_sigma[round, 4]], m[blake2b_sigma[round, 5]], 2, 6, 10, 14);
+                G(m[blake2b_sigma[round, 6]], m[blake2b_sigma[round, 7]], 3, 7, 11, 15);
+                // G apply to diagonals of internalState:
+                G(m[blake2b_sigma[round, 8]], m[blake2b_sigma[round, 9]], 0, 5, 10, 15);
+                G(m[blake2b_sigma[round, 10]], m[blake2b_sigma[round, 11]], 1, 6, 11, 12);
+                G(m[blake2b_sigma[round, 12]], m[blake2b_sigma[round, 13]], 2, 7, 8, 13);
+                G(m[blake2b_sigma[round, 14]], m[blake2b_sigma[round, 15]], 3, 4, 9, 14);
+            }
+
+            // update chain values:
+            for (int offset = 0; offset < chainValue.Length; offset++)
+            {
+                chainValue[offset] = chainValue[offset] ^ internalState[offset] ^ internalState[offset + 8];
+            }
+        }
+#endif
+
         private void G(ulong m1, ulong m2, int posA, int posB, int posC, int posD)
         {
             internalState[posA] = internalState[posA] + internalState[posB] + m1;
diff --git a/crypto/src/crypto/digests/Blake2sDigest.cs b/crypto/src/crypto/digests/Blake2sDigest.cs
index f50419126..187808aa0 100644
--- a/crypto/src/crypto/digests/Blake2sDigest.cs
+++ b/crypto/src/crypto/digests/Blake2sDigest.cs
@@ -381,6 +381,61 @@ namespace Org.BouncyCastle.Crypto.Digests
             bufferPos += offset + len - messagePos;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            if (input.IsEmpty)
+                return;
+
+            int remainingLength = 0; // left bytes of buffer
+
+            if (bufferPos != 0)
+            { // commenced, incomplete buffer
+
+                // complete the buffer:
+                remainingLength = BLOCK_LENGTH_BYTES - bufferPos;
+                if (remainingLength < input.Length)
+                { // full buffer + at least 1 byte
+                    input[..remainingLength].CopyTo(buffer.AsSpan(bufferPos));
+                    t0 += BLOCK_LENGTH_BYTES;
+                    if (t0 == 0)
+                    { // if message > 2^32
+                        t1++;
+                    }
+                    Compress(buffer, 0);
+                    bufferPos = 0;
+                    Array.Clear(buffer, 0, buffer.Length);// clear buffer
+                }
+                else
+                {
+                    input.CopyTo(buffer.AsSpan(bufferPos));
+                    bufferPos += input.Length;
+                    return;
+                }
+            }
+
+            // process blocks except last block (also if last block is full)
+            int messagePos;
+            int blockWiseLastPos = input.Length - BLOCK_LENGTH_BYTES;
+            for (messagePos = remainingLength;
+                 messagePos < blockWiseLastPos;
+                 messagePos += BLOCK_LENGTH_BYTES)
+            { // block wise 64 bytes
+                // without buffer:
+                t0 += BLOCK_LENGTH_BYTES;
+                if (t0 == 0)
+                {
+                    t1++;
+                }
+                Compress(input[messagePos..]);
+            }
+
+            // fill the buffer with left bytes, this might be a full block
+            input[messagePos..].CopyTo(buffer.AsSpan());
+            bufferPos += input.Length - messagePos;
+        }
+#endif
+
         /**
          * Close the digest, producing the final digest value. The doFinal() call
          * leaves the digest reset. Key, salt and personal string remain.
@@ -402,19 +457,44 @@ namespace Org.BouncyCastle.Crypto.Digests
             Array.Clear(buffer, 0, buffer.Length);// Holds eventually the key if input is null
             Array.Clear(internalState, 0, internalState.Length);
 
-            byte[] bytes = new byte[4];
-            for (int i = 0; i < chainValue.Length && (i * 4 < digestLength); i++)
+            int full = digestLength >> 2, partial = digestLength & 3;
+            Pack.UInt32_To_LE(chainValue, 0, full, output, outOffset);
+            if (partial > 0)
+            {
+                byte[] bytes = new byte[4];
+                Pack.UInt32_To_LE(chainValue[full], bytes, 0);
+                Array.Copy(bytes, 0, output, outOffset + digestLength - partial, partial);
+            }
+
+            Array.Clear(chainValue, 0, chainValue.Length);
+
+            Reset();
+
+            return digestLength;
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int DoFinal(Span<byte> output)
+        {
+            f0 = 0xFFFFFFFFU;
+            t0 += (uint)bufferPos;
+            // bufferPos may be < 64, so (t0 == 0) does not work
+            // for 2^32 < message length > 2^32 - 63
+            if ((t0 < 0) && (bufferPos > -t0))
             {
-                Pack.UInt32_To_LE(chainValue[i], bytes, 0);
+                t1++;
+            }
+            Compress(buffer, 0);
+            Array.Clear(buffer, 0, buffer.Length);// Holds eventually the key if input is null
+            Array.Clear(internalState, 0, internalState.Length);
 
-                if (i * 4 < digestLength - 4)
-                {
-                    Array.Copy(bytes, 0, output, outOffset + i * 4, 4);
-                }
-                else
-                {
-                    Array.Copy(bytes, 0, output, outOffset + i * 4, digestLength - (i * 4));
-                }
+            int full = digestLength >> 2, partial = digestLength & 3;
+            Pack.UInt32_To_LE(chainValue.AsSpan(0, full), output);
+            if (partial > 0)
+            {
+                Span<byte> bytes = stackalloc byte[4];
+                Pack.UInt32_To_LE(chainValue[full], bytes);
+                bytes[..partial].CopyTo(output[(digestLength - partial)..]);
             }
 
             Array.Clear(chainValue, 0, chainValue.Length);
@@ -423,6 +503,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 
             return digestLength;
         }
+#endif
 
         /**
          * Reset the digest back to its initial state. The key, the salt and the
@@ -453,9 +534,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 
             for (int round = 0; round < ROUNDS; round++)
             {
-
-                // G apply to columns of internalState:m[blake2s_sigma[round][2 *
-                // blockPos]] /+1
+                // G apply to columns of internalState: m[blake2s_sigma[round][2 * blockPos]] /+1
                 G(m[blake2s_sigma[round,0]], m[blake2s_sigma[round,1]], 0, 4, 8, 12);
                 G(m[blake2s_sigma[round,2]], m[blake2s_sigma[round,3]], 1, 5, 9, 13);
                 G(m[blake2s_sigma[round,4]], m[blake2s_sigma[round,5]], 2, 6, 10, 14);
@@ -474,6 +553,36 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void Compress(ReadOnlySpan<byte> message)
+        {
+            InitializeInternalState();
+
+            Span<uint> m = stackalloc uint[16];
+            Pack.LE_To_UInt32(message, m);
+
+            for (int round = 0; round < ROUNDS; round++)
+            {
+                // G apply to columns of internalState: m[blake2s_sigma[round][2 * blockPos]] /+1
+                G(m[blake2s_sigma[round, 0]], m[blake2s_sigma[round, 1]], 0, 4, 8, 12);
+                G(m[blake2s_sigma[round, 2]], m[blake2s_sigma[round, 3]], 1, 5, 9, 13);
+                G(m[blake2s_sigma[round, 4]], m[blake2s_sigma[round, 5]], 2, 6, 10, 14);
+                G(m[blake2s_sigma[round, 6]], m[blake2s_sigma[round, 7]], 3, 7, 11, 15);
+                // G apply to diagonals of internalState:
+                G(m[blake2s_sigma[round, 8]], m[blake2s_sigma[round, 9]], 0, 5, 10, 15);
+                G(m[blake2s_sigma[round, 10]], m[blake2s_sigma[round, 11]], 1, 6, 11, 12);
+                G(m[blake2s_sigma[round, 12]], m[blake2s_sigma[round, 13]], 2, 7, 8, 13);
+                G(m[blake2s_sigma[round, 14]], m[blake2s_sigma[round, 15]], 3, 4, 9, 14);
+            }
+
+            // update chain values:
+            for (int offset = 0; offset < chainValue.Length; offset++)
+            {
+                chainValue[offset] = chainValue[offset] ^ internalState[offset] ^ internalState[offset + 8];
+            }
+        }
+#endif
+
         private void G(uint m1, uint m2, int posA, int posB, int posC, int posD)
         {
             internalState[posA] = internalState[posA] + internalState[posB] + m1;
diff --git a/crypto/src/crypto/digests/CSHAKEDigest.cs b/crypto/src/crypto/digests/CSHAKEDigest.cs
index c3b0b7068..fc37b865c 100644
--- a/crypto/src/crypto/digests/CSHAKEDigest.cs
+++ b/crypto/src/crypto/digests/CSHAKEDigest.cs
@@ -95,6 +95,25 @@ namespace Org.BouncyCastle.Crypto.Digests
             return outLen;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int Output(Span<byte> output)
+        {
+            if (diff == null)
+            {
+                return base.Output(output);
+            }
+
+            if (!squeezing)
+            {
+                AbsorbBits(0x00, 2);
+            }
+
+            Squeeze(output);
+
+            return output.Length;
+        }
+#endif
+
         public override void Reset()
         {
             base.Reset();
diff --git a/crypto/src/crypto/digests/DSTU7564Digest.cs b/crypto/src/crypto/digests/DSTU7564Digest.cs
index b2d90799d..901e549be 100644
--- a/crypto/src/crypto/digests/DSTU7564Digest.cs
+++ b/crypto/src/crypto/digests/DSTU7564Digest.cs
@@ -1,11 +1,7 @@
 using System;
 
-using Org.BouncyCastle.Crypto.Engines;
-using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Crypto.Utilities;
-
 using Org.BouncyCastle.Utilities;
-using Org.BouncyCastle.Utilities.Encoders;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
@@ -132,25 +128,103 @@ namespace Org.BouncyCastle.Crypto.Digests
                 --length;
             }
 
-            if (length > 0)
+            while (length >= blockSize)
+            {
+                ProcessBlock(input, inOff);
+                inOff += blockSize;
+                length -= blockSize;
+                ++inputBlocks;
+            }
+
+            while (length > 0)
+            {
+                Update(input[inOff++]);
+                --length;
+            }
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            while (bufOff != 0 && input.Length > 0)
             {
-                while (length >= blockSize)
+                Update(input[0]);
+                input = input[1..];
+            }
+
+            while (input.Length >= blockSize)
+            {
+                ProcessBlock(input);
+                input = input[blockSize..];
+                ++inputBlocks;
+            }
+
+            while (input.Length > 0)
+            {
+                Update(input[0]);
+                input = input[1..];
+            }
+        }
+#endif
+
+        public virtual int DoFinal(byte[] output, int outOff)
+        {
+            // Apply padding: terminator byte and 96-bit length field
+            {
+                int inputBytes = bufOff;
+                buf[bufOff++] = (byte)0x80;
+
+                int lenPos = blockSize - 12;
+                if (bufOff > lenPos)
                 {
-                    ProcessBlock(input, inOff);
-                    inOff += blockSize;
-                    length -= blockSize;
-                    ++inputBlocks;
+                    while (bufOff < blockSize)
+                    {
+                        buf[bufOff++] = 0;
+                    }
+                    bufOff = 0;
+                    ProcessBlock(buf, 0);
                 }
 
-                while (length > 0)
+                while (bufOff < lenPos)
                 {
-                    Update(input[inOff++]);
-                    --length;
+                    buf[bufOff++] = 0;
+                }
+
+                ulong c = ((inputBlocks & 0xFFFFFFFFUL) * (ulong)blockSize + (uint)inputBytes) << 3;
+                Pack.UInt32_To_LE((uint)c, buf, bufOff);
+                bufOff += 4;
+                c >>= 32;
+                c += ((inputBlocks >> 32) * (ulong)blockSize) << 3;
+                Pack.UInt64_To_LE(c, buf, bufOff);
+                //bufOff += 8;
+                ProcessBlock(buf, 0);
+            }
+
+            {
+                Array.Copy(state, 0, tempState1, 0, columns);
+
+                P(tempState1);
+
+                for (int col = 0; col < columns; ++col)
+                {
+                    state[col] ^= tempState1[col];
                 }
             }
+
+            int neededColumns = hashSize / 8;
+            for (int col = columns - neededColumns; col < columns; ++col)
+            {
+                Pack.UInt64_To_LE(state[col], output, outOff);
+                outOff += 8;
+            }
+
+            Reset();
+
+            return hashSize;
         }
 
-        public virtual int DoFinal(byte[] output, int outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int DoFinal(Span<byte> output)
         {
             // Apply padding: terminator byte and 96-bit length field
             {
@@ -179,7 +253,7 @@ namespace Org.BouncyCastle.Crypto.Digests
                 c >>= 32;
                 c += ((inputBlocks >> 32) * (ulong)blockSize) << 3;
                 Pack.UInt64_To_LE(c, buf, bufOff);
-    //            bufOff += 8;
+                //bufOff += 8;
                 ProcessBlock(buf, 0);
             }
 
@@ -197,14 +271,15 @@ namespace Org.BouncyCastle.Crypto.Digests
             int neededColumns = hashSize / 8;
             for (int col = columns - neededColumns; col < columns; ++col)
             {
-                Pack.UInt64_To_LE(state[col], output, outOff);
-                outOff += 8;
+                Pack.UInt64_To_LE(state[col], output);
+                output = output[8..];
             }
 
             Reset();
 
             return hashSize;
         }
+#endif
 
         public virtual void Reset()
         {
@@ -236,6 +311,28 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        protected virtual void ProcessBlock(ReadOnlySpan<byte> input)
+        {
+            for (int col = 0; col < columns; ++col)
+            {
+                ulong word = Pack.LE_To_UInt64(input);
+                input = input[8..];
+
+                tempState1[col] = state[col] ^ word;
+                tempState2[col] = word;
+            }
+
+            P(tempState1);
+            Q(tempState2);
+
+            for (int col = 0; col < columns; ++col)
+            {
+                state[col] ^= tempState1[col] ^ tempState2[col];
+            }
+        }
+#endif
+
         private void P(ulong[] s)
         {
             for (int round = 0; round < rounds; ++round)
diff --git a/crypto/src/crypto/digests/GOST3411Digest.cs b/crypto/src/crypto/digests/GOST3411Digest.cs
index 123751baa..dc1d376c3 100644
--- a/crypto/src/crypto/digests/GOST3411Digest.cs
+++ b/crypto/src/crypto/digests/GOST3411Digest.cs
@@ -91,10 +91,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 			byteCount++;
 		}
 
-		public void BlockUpdate(
-			byte[]	input,
-			int		inOff,
-			int		length)
+		public void BlockUpdate(byte[] input, int inOff, int length)
 		{
 			while ((xBufOff != 0) && (length > 0))
 			{
@@ -123,6 +120,34 @@ namespace Org.BouncyCastle.Crypto.Digests
 			}
 		}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public void BlockUpdate(ReadOnlySpan<byte> input)
+		{
+			while ((xBufOff != 0) && (input.Length > 0))
+			{
+				Update(input[0]);
+				input = input[1..];
+			}
+
+			while (input.Length >= xBuf.Length)
+			{
+				input[..xBuf.Length].CopyTo(xBuf.AsSpan());
+
+				sumByteArray(xBuf); // calc sum M
+				processBlock(xBuf, 0);
+				input = input[xBuf.Length..];
+				byteCount += (uint)xBuf.Length;
+			}
+
+			// load in the remainder.
+			while (input.Length > 0)
+			{
+				Update(input[0]);
+				input = input[1..];
+			}
+		}
+#endif
+
 		// (i + 1 + 4(k - 1)) = 8i + k      i = 0-3, k = 1-8
 		private byte[] K = new byte[32];
 
@@ -234,7 +259,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 			Array.Copy(S, 0, H, 0, H.Length);
 		}
 
-		private void finish()
+		private void Finish()
 		{
 			ulong bitCount = byteCount * 8;
 			Pack.UInt64_To_LE(bitCount, L);
@@ -248,11 +273,9 @@ namespace Org.BouncyCastle.Crypto.Digests
 			processBlock(Sum, 0);
 		}
 
-		public int DoFinal(
-			byte[]  output,
-			int     outOff)
+		public int DoFinal(byte[] output, int outOff)
 		{
-			finish();
+			Finish();
 
 			H.CopyTo(output, outOff);
 
@@ -261,6 +284,19 @@ namespace Org.BouncyCastle.Crypto.Digests
 			return DIGEST_LENGTH;
 		}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public int DoFinal(Span<byte> output)
+		{
+			Finish();
+
+			H.CopyTo(output);
+
+			Reset();
+
+			return DIGEST_LENGTH;
+		}
+#endif
+
 		/**
 		* reset the chaining variables to the IV values.
 		*/
diff --git a/crypto/src/crypto/digests/GOST3411_2012Digest.cs b/crypto/src/crypto/digests/GOST3411_2012Digest.cs
index 68cb6c035..259f4bcae 100644
--- a/crypto/src/crypto/digests/GOST3411_2012Digest.cs
+++ b/crypto/src/crypto/digests/GOST3411_2012Digest.cs
@@ -1,10 +1,11 @@
 using System;
-using Org.BouncyCastle.Crypto;
+
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
-    public abstract class Gost3411_2012Digest:IDigest,IMemoable
+    public abstract class Gost3411_2012Digest
+        : IDigest, IMemoable
     {
         private readonly byte[] IV = new byte[64];
         private readonly byte[] N = new byte[64];
@@ -21,8 +22,8 @@ namespace Org.BouncyCastle.Crypto.Digests
 
         protected Gost3411_2012Digest(byte[] IV)
         {
-            System.Array.Copy(IV,this.IV,64);
-            System.Array.Copy(IV, h, 64);
+            Array.Copy(IV,this.IV,64);
+            Array.Copy(IV, h, 64);
         }
 
         public abstract string AlgorithmName { get; }
@@ -43,7 +44,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 
             if (bOff != 64)
             {
-                System.Array.Copy(block, bOff, m, 64 - lenM, lenM);
+                Array.Copy(block, bOff, m, 64 - lenM, lenM);
             }
 
             g_N(h, N, m);
@@ -60,6 +61,39 @@ namespace Org.BouncyCastle.Crypto.Digests
             return 64;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int DoFinal(Span<byte> output)
+        {
+            int lenM = 64 - bOff;
+
+            // At this point it is certain that lenM is smaller than 64
+            for (int i = 0; i != 64 - lenM; i++)
+            {
+                m[i] = 0;
+            }
+
+            m[63 - lenM] = 1;
+
+            if (bOff != 64)
+            {
+                Array.Copy(block, bOff, m, 64 - lenM, lenM);
+            }
+
+            g_N(h, N, m);
+            addMod512(N, lenM * 8);
+            addMod512(Sigma, m);
+            g_N(h, Zero, N);
+            g_N(h, Zero, Sigma);
+
+            reverse(h, tmp);
+
+            tmp.CopyTo(output);
+
+            Reset();
+            return 64;
+        }
+#endif
+
         public int GetByteLength()
         {
             return 64;
@@ -73,7 +107,7 @@ namespace Org.BouncyCastle.Crypto.Digests
             bOff = 64;
             Arrays.Fill(N, (byte)0);
             Arrays.Fill(Sigma, (byte)0);
-            System.Array.Copy(IV, 0, h, 0, 64);
+            Array.Copy(IV, 0, h, 0, 64);
             Arrays.Fill(block, (byte)0);
         }
 
@@ -81,14 +115,14 @@ namespace Org.BouncyCastle.Crypto.Digests
         {
             Gost3411_2012Digest o = (Gost3411_2012Digest)other;
 
-            System.Array.Copy(o.IV, 0, this.IV, 0, 64);
-            System.Array.Copy(o.N, 0, this.N, 0, 64);
-            System.Array.Copy(o.Sigma, 0, this.Sigma, 0, 64);
-            System.Array.Copy(o.Ki, 0, this.Ki, 0, 64);
-            System.Array.Copy(o.m, 0, this.m, 0, 64);
-            System.Array.Copy(o.h, 0, this.h, 0, 64);
+            Array.Copy(o.IV, 0, this.IV, 0, 64);
+            Array.Copy(o.N, 0, this.N, 0, 64);
+            Array.Copy(o.Sigma, 0, this.Sigma, 0, 64);
+            Array.Copy(o.Ki, 0, this.Ki, 0, 64);
+            Array.Copy(o.m, 0, this.m, 0, 64);
+            Array.Copy(o.h, 0, this.h, 0, 64);
 
-            System.Array.Copy(o.block, 0, this.block, 0, 64);
+            Array.Copy(o.block, 0, this.block, 0, 64);
             this.bOff = o.bOff;
         }
 
@@ -104,7 +138,6 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
-
         public void BlockUpdate(byte[] input, int inOff, int len)
         {
             while (bOff != 64 && len > 0)
@@ -114,7 +147,7 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
             while (len >= 64)
             {
-                System.Array.Copy(input, inOff, tmp, 0, 64);
+                Array.Copy(input, inOff, tmp, 0, 64);
                 reverse(tmp, block);
                 g_N(h, N, block);
                 addMod512(N, 512);
@@ -130,8 +163,31 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
-    
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            while (bOff != 64 && input.Length > 0)
+            {
+                Update(input[0]);
+                input = input[1..];
+            }
+            while (input.Length >= 64)
+            {
+                input[..64].CopyTo(tmp.AsSpan());
+                reverse(tmp, block);
+                g_N(h, N, block);
+                addMod512(N, 512);
+                addMod512(Sigma, block);
 
+                input = input[64..];
+            }
+            while (input.Length > 0)
+            {
+                Update(input[0]);
+                input = input[1..];
+            }
+        }
+#endif
 
         private void F(byte[] V)
         {
@@ -317,7 +373,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 
         private void E(byte[] K, byte[] m)
         {
-            System.Array.Copy(K, 0, Ki, 0, 64);
+            Array.Copy(K, 0, Ki, 0, 64);
             xor512(K, m);
             F(K);
             for (int i = 0; i < 11; ++i)
@@ -334,7 +390,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 
         private void g_N(byte[] h, byte[] N, byte[] m)
         {
-            System.Array.Copy(h, 0, tmp, 0, 64);
+            Array.Copy(h, 0, tmp, 0, 64);
 
             xor512(h, N);
             F(h);
diff --git a/crypto/src/crypto/digests/GOST3411_2012_256Digest.cs b/crypto/src/crypto/digests/GOST3411_2012_256Digest.cs
index 77cf6c50f..47877b2b5 100644
--- a/crypto/src/crypto/digests/GOST3411_2012_256Digest.cs
+++ b/crypto/src/crypto/digests/GOST3411_2012_256Digest.cs
@@ -46,6 +46,18 @@ namespace Org.BouncyCastle.Crypto.Digests
 			return 32;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
+        {
+            Span<byte> result = stackalloc byte[64];
+            base.DoFinal(result);
+
+            result[32..].CopyTo(output);
+
+            return 32;
+        }
+#endif
+
         public override IMemoable Copy()
         {
 			return new Gost3411_2012_256Digest(this);
diff --git a/crypto/src/crypto/digests/GeneralDigest.cs b/crypto/src/crypto/digests/GeneralDigest.cs
index c38b35fd3..02bad2cfb 100644
--- a/crypto/src/crypto/digests/GeneralDigest.cs
+++ b/crypto/src/crypto/digests/GeneralDigest.cs
@@ -95,6 +95,50 @@ namespace Org.BouncyCastle.Crypto.Digests
             byteCount += length;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            int length = input.Length;
+
+            //
+            // fill the current word
+            //
+            int i = 0;
+            if (xBufOff != 0)
+            {
+                while (i < length)
+                {
+                    xBuf[xBufOff++] = input[i++];
+                    if (xBufOff == 4)
+                    {
+                        ProcessWord(xBuf, 0);
+                        xBufOff = 0;
+                        break;
+                    }
+                }
+            }
+
+            //
+            // process whole words.
+            //
+            int limit = length - 3;
+            for (; i < limit; i += 4)
+            {
+                ProcessWord(input.Slice(i, 4));
+            }
+
+            //
+            // load in the remainder.
+            //
+            while (i < length)
+            {
+                xBuf[xBufOff++] = input[i++];
+            }
+
+            byteCount += length;
+        }
+#endif
+
         public void Finish()
         {
             long    bitLength = (byteCount << 3);
@@ -122,6 +166,9 @@ namespace Org.BouncyCastle.Crypto.Digests
 		}
 
 		internal abstract void ProcessWord(byte[] input, int inOff);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal abstract void ProcessWord(ReadOnlySpan<byte> word);
+#endif
         internal abstract void ProcessLength(long bitLength);
         internal abstract void ProcessBlock();
         public abstract string AlgorithmName { get; }
@@ -129,5 +176,8 @@ namespace Org.BouncyCastle.Crypto.Digests
         public abstract int DoFinal(byte[] output, int outOff);
 		public abstract IMemoable Copy();
 		public abstract void Reset(IMemoable t);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public abstract int DoFinal(Span<byte> output);
+#endif
     }
 }
diff --git a/crypto/src/crypto/digests/Haraka256Digest.cs b/crypto/src/crypto/digests/Haraka256Digest.cs
index f7af4bf69..27aea9b29 100644
--- a/crypto/src/crypto/digests/Haraka256Digest.cs
+++ b/crypto/src/crypto/digests/Haraka256Digest.cs
@@ -93,7 +93,58 @@ namespace Org.BouncyCastle.Crypto.Digests
             return DIGEST_SIZE;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private int haraka256256(byte[] msg, Span<byte> output)
+        {
+            byte[][] s1 = new byte[2][];
+            s1[0] = new byte[16];
+            s1[1] = new byte[16];
+            byte[][] s2 = new byte[2][];
+            s2[0] = new byte[16];
+            s2[1] = new byte[16];
+
+            Array.Copy(msg, 0, s1[0], 0, 16);
+            Array.Copy(msg, 16, s1[1], 0, 16);
+
+            s1[0] = aesEnc(s1[0], RC[0]);
+            s1[1] = aesEnc(s1[1], RC[1]);
+            s1[0] = aesEnc(s1[0], RC[2]);
+            s1[1] = aesEnc(s1[1], RC[3]);
+            mix256(s1, s2);
+
+            s1[0] = aesEnc(s2[0], RC[4]);
+            s1[1] = aesEnc(s2[1], RC[5]);
+            s1[0] = aesEnc(s1[0], RC[6]);
+            s1[1] = aesEnc(s1[1], RC[7]);
+            mix256(s1, s2);
+
+            s1[0] = aesEnc(s2[0], RC[8]);
+            s1[1] = aesEnc(s2[1], RC[9]);
+            s1[0] = aesEnc(s1[0], RC[10]);
+            s1[1] = aesEnc(s1[1], RC[11]);
+            mix256(s1, s2);
 
+            s1[0] = aesEnc(s2[0], RC[12]);
+            s1[1] = aesEnc(s2[1], RC[13]);
+            s1[0] = aesEnc(s1[0], RC[14]);
+            s1[1] = aesEnc(s1[1], RC[15]);
+            mix256(s1, s2);
+
+            s1[0] = aesEnc(s2[0], RC[16]);
+            s1[1] = aesEnc(s2[1], RC[17]);
+            s1[0] = aesEnc(s1[0], RC[18]);
+            s1[1] = aesEnc(s1[1], RC[19]);
+            mix256(s1, s2);
+
+            s1[0] = Xor(s2[0], msg, 0);
+            s1[1] = Xor(s2[1], msg, 16);
+
+            s1[0].AsSpan(0, 16).CopyTo(output);
+            s1[1].AsSpan(0, 16).CopyTo(output[16..]);
+
+            return DIGEST_SIZE;
+        }
+#endif
 
         public Haraka256Digest()
         {
@@ -106,10 +157,7 @@ namespace Org.BouncyCastle.Crypto.Digests
             this.off = digest.off;
         }
 
-        public string getAlgorithmName()
-        {
-            return "Haraka-256";
-        }
+        public override string AlgorithmName => "Haraka-256";
 
         public override void Update(byte input)
         {
@@ -132,6 +180,19 @@ namespace Org.BouncyCastle.Crypto.Digests
             off += len;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            if (off + input.Length > 32)
+            {
+                throw new ArgumentException("total input cannot be more than 32 bytes");
+            }
+
+            input.CopyTo(buffer.AsSpan(off));
+            off += input.Length;
+        }
+#endif
+
         public override int DoFinal(byte[] output, int outOff)
         {
             if (off != 32)
@@ -151,6 +212,27 @@ namespace Org.BouncyCastle.Crypto.Digests
             return rv;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
+        {
+            if (off != 32)
+            {
+                throw new ArgumentException("input must be exactly 32 bytes");
+            }
+
+            if (output.Length < 32)
+            {
+                throw new ArgumentException("output too short to receive digest");
+            }
+
+            int rv = haraka256256(buffer, output);
+
+            Reset();
+
+            return rv;
+        }
+#endif
+
         public override void Reset()
         {
             off = 0;
diff --git a/crypto/src/crypto/digests/Haraka512Digest.cs b/crypto/src/crypto/digests/Haraka512Digest.cs
index 36bdcbabe..0faeae710 100644
--- a/crypto/src/crypto/digests/Haraka512Digest.cs
+++ b/crypto/src/crypto/digests/Haraka512Digest.cs
@@ -1,5 +1,7 @@
 using System;
 
+using Org.BouncyCastle.Utilities;
+
 namespace Org.BouncyCastle.Crypto.Digests
 {
     public class Haraka512Digest : HarakaBase
@@ -167,10 +169,92 @@ namespace Org.BouncyCastle.Crypto.Digests
             return DIGEST_SIZE;
         }
 
-        public string GetAlgorithmName()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private int Haraka512256(byte[] msg, Span<byte> output)
         {
-            return "Haraka-512";
+            byte[][] s1 = new byte[4][];
+            s1[0] = new byte[16];
+            s1[1] = new byte[16];
+            s1[2] = new byte[16];
+            s1[3] = new byte[16];
+            byte[][] s2 = new byte[4][];
+            s2[0] = new byte[16];
+            s2[1] = new byte[16];
+            s2[2] = new byte[16];
+            s2[3] = new byte[16];
+
+            //-- Unrolled version of above.
+
+            Array.Copy(msg, 0, s1[0], 0, 16);
+            Array.Copy(msg, 16, s1[1], 0, 16);
+            Array.Copy(msg, 32, s1[2], 0, 16);
+            Array.Copy(msg, 48, s1[3], 0, 16);
+
+            s1[0] = aesEnc(s1[0], RC[0]);
+            s1[1] = aesEnc(s1[1], RC[1]);
+            s1[2] = aesEnc(s1[2], RC[2]);
+            s1[3] = aesEnc(s1[3], RC[3]);
+            s1[0] = aesEnc(s1[0], RC[4]);
+            s1[1] = aesEnc(s1[1], RC[5]);
+            s1[2] = aesEnc(s1[2], RC[6]);
+            s1[3] = aesEnc(s1[3], RC[7]);
+            Mix512(s1, s2);
+
+            s1[0] = aesEnc(s2[0], RC[8]);
+            s1[1] = aesEnc(s2[1], RC[9]);
+            s1[2] = aesEnc(s2[2], RC[10]);
+            s1[3] = aesEnc(s2[3], RC[11]);
+            s1[0] = aesEnc(s1[0], RC[12]);
+            s1[1] = aesEnc(s1[1], RC[13]);
+            s1[2] = aesEnc(s1[2], RC[14]);
+            s1[3] = aesEnc(s1[3], RC[15]);
+            Mix512(s1, s2);
+
+            s1[0] = aesEnc(s2[0], RC[16]);
+            s1[1] = aesEnc(s2[1], RC[17]);
+            s1[2] = aesEnc(s2[2], RC[18]);
+            s1[3] = aesEnc(s2[3], RC[19]);
+            s1[0] = aesEnc(s1[0], RC[20]);
+            s1[1] = aesEnc(s1[1], RC[21]);
+            s1[2] = aesEnc(s1[2], RC[22]);
+            s1[3] = aesEnc(s1[3], RC[23]);
+            Mix512(s1, s2);
+
+            s1[0] = aesEnc(s2[0], RC[24]);
+            s1[1] = aesEnc(s2[1], RC[25]);
+            s1[2] = aesEnc(s2[2], RC[26]);
+            s1[3] = aesEnc(s2[3], RC[27]);
+            s1[0] = aesEnc(s1[0], RC[28]);
+            s1[1] = aesEnc(s1[1], RC[29]);
+            s1[2] = aesEnc(s1[2], RC[30]);
+            s1[3] = aesEnc(s1[3], RC[31]);
+            Mix512(s1, s2);
+
+            s1[0] = aesEnc(s2[0], RC[32]);
+            s1[1] = aesEnc(s2[1], RC[33]);
+            s1[2] = aesEnc(s2[2], RC[34]);
+            s1[3] = aesEnc(s2[3], RC[35]);
+            s1[0] = aesEnc(s1[0], RC[36]);
+            s1[1] = aesEnc(s1[1], RC[37]);
+            s1[2] = aesEnc(s1[2], RC[38]);
+            s1[3] = aesEnc(s1[3], RC[39]);
+            Mix512(s1, s2);
+
+            s1[0] = Xor(s2[0], msg, 0);
+            s1[1] = Xor(s2[1], msg, 16);
+            s1[2] = Xor(s2[2], msg, 32);
+            s1[3] = Xor(s2[3], msg, 48);
+
+            s1[0].AsSpan(8, 8).CopyTo(output);
+            s1[1].AsSpan(8, 8).CopyTo(output[8..]);
+            s1[2].AsSpan(0, 8).CopyTo(output[16..]);
+            s1[3].AsSpan(0, 8).CopyTo(output[24..]);
+
+            return DIGEST_SIZE;
         }
+#endif
+
+        public override string AlgorithmName => "Haraka-512";
 
         public override void Update(byte input)
         {
@@ -193,6 +277,19 @@ namespace Org.BouncyCastle.Crypto.Digests
             off += len;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            if (off + input.Length > 64)
+            {
+                throw new ArgumentException("total input cannot be more than 64 bytes");
+            }
+
+            input.CopyTo(buffer.AsSpan(off));
+            off += input.Length;
+        }
+#endif
+
         public override int DoFinal(byte[] output, int outOff)
         {
             if (off != 64)
@@ -212,6 +309,27 @@ namespace Org.BouncyCastle.Crypto.Digests
             return rv;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
+        {
+            if (off != 64)
+            {
+                throw new ArgumentException("input must be exactly 64 bytes");
+            }
+
+            if (output.Length < 32)
+            {
+                throw new ArgumentException("output too short to receive digest");
+            }
+
+            int rv = Haraka512256(buffer, output);
+
+            Reset();
+
+            return rv;
+        }
+#endif
+
         public override void Reset()
         {
             off = 0;
diff --git a/crypto/src/crypto/digests/HarakaBase.cs b/crypto/src/crypto/digests/HarakaBase.cs
index 6157198f1..1270de35c 100644
--- a/crypto/src/crypto/digests/HarakaBase.cs
+++ b/crypto/src/crypto/digests/HarakaBase.cs
@@ -1,6 +1,4 @@
 using System;
-using System.Collections.Generic;
-using System.Text;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
@@ -26,10 +24,7 @@ namespace Org.BouncyCastle.Crypto.Digests
         {(byte)0xe1, (byte)0xf8, (byte)0x98, (byte)0x11, (byte)0x69, (byte)0xd9, (byte)0x8e, (byte)0x94, (byte)0x9b, (byte)0x1e, (byte)0x87, (byte)0xe9, (byte)0xce, (byte)0x55, (byte)0x28, (byte)0xdf},
         {(byte)0x8c, (byte)0xa1, (byte)0x89, (byte)0x0d, (byte)0xbf, (byte)0xe6, (byte)0x42, (byte)0x68, (byte)0x41, (byte)0x99, (byte)0x2d, (byte)0x0f, (byte)0xb0, (byte)0x54, (byte)0xbb, (byte)0x16}};
 
-        public string AlgorithmName
-        {
-            get { return "Haraka Base"; }
-        }
+        public abstract string AlgorithmName { get; }
 
         static byte sBox(byte x)
         {
@@ -144,5 +139,10 @@ namespace Org.BouncyCastle.Crypto.Digests
 
         public abstract void Reset();
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public abstract void BlockUpdate(ReadOnlySpan<byte> input);
+
+        public abstract int DoFinal(Span<byte> output);
+#endif
     }
 }
diff --git a/crypto/src/crypto/digests/KeccakDigest.cs b/crypto/src/crypto/digests/KeccakDigest.cs
index 2da2e099e..b8305cc13 100644
--- a/crypto/src/crypto/digests/KeccakDigest.cs
+++ b/crypto/src/crypto/digests/KeccakDigest.cs
@@ -76,6 +76,13 @@ namespace Org.BouncyCastle.Crypto.Digests
             Absorb(input, inOff, len);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            Absorb(input);
+        }
+#endif
+
         public virtual int DoFinal(byte[] output, int outOff)
         {
             Squeeze(output, outOff, fixedOutputLength);
@@ -85,6 +92,18 @@ namespace Org.BouncyCastle.Crypto.Digests
             return GetDigestSize();
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int DoFinal(Span<byte> output)
+        {
+            int digestSize = GetDigestSize();
+            Squeeze(output[..digestSize]);
+
+            Reset();
+
+            return digestSize;
+        }
+#endif
+
         /*
          * TODO Possible API change to support partial-byte suffixes.
          */
@@ -199,6 +218,46 @@ namespace Org.BouncyCastle.Crypto.Digests
             this.bitsInQueue = remaining << 3;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        protected void Absorb(ReadOnlySpan<byte> data)
+        {
+            if ((bitsInQueue & 7) != 0)
+                throw new InvalidOperationException("attempt to absorb with odd length queue");
+            if (squeezing)
+                throw new InvalidOperationException("attempt to absorb while squeezing");
+
+            int bytesInQueue = bitsInQueue >> 3;
+            int rateBytes = rate >> 3;
+
+            int len = data.Length;
+            int available = rateBytes - bytesInQueue;
+            if (len < available)
+            {
+                data.CopyTo(dataQueue.AsSpan(bytesInQueue));
+                this.bitsInQueue += len << 3;
+                return;
+            }
+
+            int count = 0;
+            if (bytesInQueue > 0)
+            {
+                data[..available].CopyTo(dataQueue.AsSpan(bytesInQueue));
+                count += available;
+                KeccakAbsorb(dataQueue, 0);
+            }
+
+            int remaining;
+            while ((remaining = len - count) >= rateBytes)
+            {
+                KeccakAbsorb(data[count..]);
+                count += rateBytes;
+            }
+
+            data[count..].CopyTo(dataQueue.AsSpan());
+            this.bitsInQueue = remaining << 3;
+        }
+#endif
+
         protected void AbsorbBits(int data, int bits)
         {
             if (bits < 1 || bits > 7)
@@ -270,6 +329,30 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        protected void Squeeze(Span<byte> output)
+        {
+            if (!squeezing)
+            {
+                PadAndSwitchToSqueezingPhase();
+            }
+            long outputLength = (long)output.Length << 3;
+
+            long i = 0;
+            while (i < outputLength)
+            {
+                if (bitsInQueue == 0)
+                {
+                    KeccakExtract();
+                }
+                int partialBlock = (int)System.Math.Min(bitsInQueue, outputLength - i);
+                dataQueue.AsSpan((rate - bitsInQueue) >> 3, partialBlock >> 3).CopyTo(output[(int)(i >> 3)..]);
+                bitsInQueue -= partialBlock;
+                i += partialBlock;
+            }
+        }
+#endif
+
         private void KeccakAbsorb(byte[] data, int off)
         {
             int count = rate >> 6;
@@ -282,6 +365,20 @@ namespace Org.BouncyCastle.Crypto.Digests
             KeccakPermutation();
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void KeccakAbsorb(ReadOnlySpan<byte> data)
+        {
+            int count = rate >> 6, off = 0;
+            for (int i = 0; i < count; ++i)
+            {
+                state[i] ^= Pack.LE_To_UInt64(data[off..]);
+                off += 8;
+            }
+
+            KeccakPermutation();
+        }
+#endif
+
         private void KeccakExtract()
         {
             KeccakPermutation();
diff --git a/crypto/src/crypto/digests/LongDigest.cs b/crypto/src/crypto/digests/LongDigest.cs
index aaa0b43ce..6a2f94ece 100644
--- a/crypto/src/crypto/digests/LongDigest.cs
+++ b/crypto/src/crypto/digests/LongDigest.cs
@@ -10,46 +10,46 @@ namespace Org.BouncyCastle.Crypto.Digests
     * Base class for SHA-384 and SHA-512.
     */
     public abstract class LongDigest
-		: IDigest, IMemoable
+        : IDigest, IMemoable
     {
-        private int     MyByteLength = 128;
+        private int MyByteLength = 128;
 
-        private byte[]  xBuf;
-        private int     xBufOff;
+        private byte[] xBuf;
+        private int xBufOff;
 
-        private long	byteCount1;
-        private long	byteCount2;
+        private long byteCount1;
+        private long byteCount2;
 
         internal ulong H1, H2, H3, H4, H5, H6, H7, H8;
 
         private ulong[] W = new ulong[80];
         private int wOff;
 
-		/**
+        /**
         * Constructor for variable length word
         */
         internal LongDigest()
         {
             xBuf = new byte[8];
 
-			Reset();
+            Reset();
         }
 
-		/**
+        /**
         * Copy constructor.  We are using copy constructors in place
         * of the object.Clone() interface as this interface is not
         * supported by J2ME.
         */
         internal LongDigest(
-			LongDigest t)
-		{
-			xBuf = new byte[t.xBuf.Length];
+            LongDigest t)
+        {
+            xBuf = new byte[t.xBuf.Length];
 
-			CopyIn(t);
-		}
+            CopyIn(t);
+        }
 
-		protected void CopyIn(LongDigest t)
-		{
+        protected void CopyIn(LongDigest t)
+        {
             Array.Copy(t.xBuf, 0, xBuf, 0, t.xBuf.Length);
 
             xBufOff = t.xBufOff;
@@ -84,9 +84,9 @@ namespace Org.BouncyCastle.Crypto.Digests
         }
 
         public void BlockUpdate(
-            byte[]  input,
-            int     inOff,
-            int     length)
+            byte[] input,
+            int inOff,
+            int length)
         {
             //
             // fill the current word
@@ -123,12 +123,54 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            int inOff = 0;
+            int length = input.Length;
+
+            //
+            // fill the current word
+            //
+            while ((xBufOff != 0) && (length > 0))
+            {
+                Update(input[inOff]);
+
+                inOff++;
+                length--;
+            }
+
+            //
+            // process whole words.
+            //
+            while (length >= xBuf.Length)
+            {
+                ProcessWord(input.Slice(inOff, xBuf.Length));
+
+                inOff += xBuf.Length;
+                length -= xBuf.Length;
+                byteCount1 += xBuf.Length;
+            }
+
+            //
+            // load in the remainder.
+            //
+            while (length > 0)
+            {
+                Update(input[inOff]);
+
+                inOff++;
+                length--;
+            }
+        }
+#endif
+
         public void Finish()
         {
             AdjustByteCounts();
 
-            long    lowBitLength = byteCount1 << 3;
-            long    hiBitLength = byteCount2;
+            long lowBitLength = byteCount1 << 3;
+            long hiBitLength = byteCount2;
 
             //
             // add the pad bytes.
@@ -151,20 +193,20 @@ namespace Org.BouncyCastle.Crypto.Digests
             byteCount2 = 0;
 
             xBufOff = 0;
-            for ( int i = 0; i < xBuf.Length; i++ )
+            for (int i = 0; i < xBuf.Length; i++)
             {
                 xBuf[i] = 0;
             }
 
             wOff = 0;
-			Array.Clear(W, 0, W.Length);
+            Array.Clear(W, 0, W.Length);
         }
 
         internal void ProcessWord(
-            byte[]  input,
-            int     inOff)
+            byte[] input,
+            int inOff)
         {
-			W[wOff] = Pack.BE_To_UInt64(input, inOff);
+            W[wOff] = Pack.BE_To_UInt64(input, inOff);
 
             if (++wOff == 16)
             {
@@ -172,6 +214,18 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal void ProcessWord(ReadOnlySpan<byte> word)
+        {
+            W[wOff] = Pack.BE_To_UInt64(word);
+
+            if (++wOff == 16)
+            {
+                ProcessBlock();
+            }
+        }
+#endif
+
         /**
         * adjust the byte counts so that byteCount2 represents the
         * upper long (less 3 bits) word of the byte count.
@@ -180,14 +234,14 @@ namespace Org.BouncyCastle.Crypto.Digests
         {
             if (byteCount1 > 0x1fffffffffffffffL)
             {
-                byteCount2 += (long) ((ulong) byteCount1 >> 61);
+                byteCount2 += (long)((ulong)byteCount1 >> 61);
                 byteCount1 &= 0x1fffffffffffffffL;
             }
         }
 
         internal void ProcessLength(
-            long	lowW,
-            long	hiW)
+            long lowW,
+            long hiW)
         {
             if (wOff > 14)
             {
@@ -222,51 +276,51 @@ namespace Org.BouncyCastle.Crypto.Digests
             ulong g = H7;
             ulong h = H8;
 
-			int t = 0;
-			for(int i = 0; i < 10; i ++)
-			{
-				// t = 8 * i
-				h += Sum1(e) + Ch(e, f, g) + K[t] + W[t++];
-				d += h;
-				h += Sum0(a) + Maj(a, b, c);
-
-				// t = 8 * i + 1
-				g += Sum1(d) + Ch(d, e, f) + K[t] + W[t++];
-				c += g;
-				g += Sum0(h) + Maj(h, a, b);
-
-				// t = 8 * i + 2
-				f += Sum1(c) + Ch(c, d, e) + K[t] + W[t++];
-				b += f;
-				f += Sum0(g) + Maj(g, h, a);
-
-				// t = 8 * i + 3
-				e += Sum1(b) + Ch(b, c, d) + K[t] + W[t++];
-				a += e;
-				e += Sum0(f) + Maj(f, g, h);
-
-				// t = 8 * i + 4
-				d += Sum1(a) + Ch(a, b, c) + K[t] + W[t++];
-				h += d;
-				d += Sum0(e) + Maj(e, f, g);
-
-				// t = 8 * i + 5
-				c += Sum1(h) + Ch(h, a, b) + K[t] + W[t++];
-				g += c;
-				c += Sum0(d) + Maj(d, e, f);
-
-				// t = 8 * i + 6
-				b += Sum1(g) + Ch(g, h, a) + K[t] + W[t++];
-				f += b;
-				b += Sum0(c) + Maj(c, d, e);
-
-				// t = 8 * i + 7
-				a += Sum1(f) + Ch(f, g, h) + K[t] + W[t++];
-				e += a;
-				a += Sum0(b) + Maj(b, c, d);
-			}
-
-			H1 += a;
+            int t = 0;
+            for (int i = 0; i < 10; i++)
+            {
+                // t = 8 * i
+                h += Sum1(e) + Ch(e, f, g) + K[t] + W[t++];
+                d += h;
+                h += Sum0(a) + Maj(a, b, c);
+
+                // t = 8 * i + 1
+                g += Sum1(d) + Ch(d, e, f) + K[t] + W[t++];
+                c += g;
+                g += Sum0(h) + Maj(h, a, b);
+
+                // t = 8 * i + 2
+                f += Sum1(c) + Ch(c, d, e) + K[t] + W[t++];
+                b += f;
+                f += Sum0(g) + Maj(g, h, a);
+
+                // t = 8 * i + 3
+                e += Sum1(b) + Ch(b, c, d) + K[t] + W[t++];
+                a += e;
+                e += Sum0(f) + Maj(f, g, h);
+
+                // t = 8 * i + 4
+                d += Sum1(a) + Ch(a, b, c) + K[t] + W[t++];
+                h += d;
+                d += Sum0(e) + Maj(e, f, g);
+
+                // t = 8 * i + 5
+                c += Sum1(h) + Ch(h, a, b) + K[t] + W[t++];
+                g += c;
+                c += Sum0(d) + Maj(d, e, f);
+
+                // t = 8 * i + 6
+                b += Sum1(g) + Ch(g, h, a) + K[t] + W[t++];
+                f += b;
+                b += Sum0(c) + Maj(c, d, e);
+
+                // t = 8 * i + 7
+                a += Sum1(f) + Ch(f, g, h) + K[t] + W[t++];
+                e += a;
+                a += Sum0(b) + Maj(b, c, d);
+            }
+
+            H1 += a;
             H2 += b;
             H3 += c;
             H4 += d;
@@ -275,14 +329,14 @@ namespace Org.BouncyCastle.Crypto.Digests
             H7 += g;
             H8 += h;
 
-			//
+            //
             // reset the offset and clean out the word buffer.
             //
             wOff = 0;
-			Array.Clear(W, 0, 16);
-		}
+            Array.Clear(W, 0, 16);
+        }
 
-		/* SHA-384 and SHA-512 functions (as for SHA-256 but for longs) */
+        /* SHA-384 and SHA-512 functions (as for SHA-256 but for longs) */
         private static ulong Ch(ulong x, ulong y, ulong z)
         {
             return (x & y) ^ (~x & z);
@@ -295,61 +349,64 @@ namespace Org.BouncyCastle.Crypto.Digests
 
         private static ulong Sum0(ulong x)
         {
-	        return ((x << 36) | (x >> 28)) ^ ((x << 30) | (x >> 34)) ^ ((x << 25) | (x >> 39));
+            return ((x << 36) | (x >> 28)) ^ ((x << 30) | (x >> 34)) ^ ((x << 25) | (x >> 39));
         }
 
-		private static ulong Sum1(ulong x)
+        private static ulong Sum1(ulong x)
         {
-	        return ((x << 50) | (x >> 14)) ^ ((x << 46) | (x >> 18)) ^ ((x << 23) | (x >> 41));
+            return ((x << 50) | (x >> 14)) ^ ((x << 46) | (x >> 18)) ^ ((x << 23) | (x >> 41));
         }
 
         private static ulong Sigma0(ulong x)
         {
-	        return ((x << 63) | (x >> 1)) ^ ((x << 56) | (x >> 8)) ^ (x >> 7);
+            return ((x << 63) | (x >> 1)) ^ ((x << 56) | (x >> 8)) ^ (x >> 7);
         }
 
         private static ulong Sigma1(ulong x)
         {
-	        return ((x << 45) | (x >> 19)) ^ ((x << 3) | (x >> 61)) ^ (x >> 6);
+            return ((x << 45) | (x >> 19)) ^ ((x << 3) | (x >> 61)) ^ (x >> 6);
         }
 
         /* SHA-384 and SHA-512 Constants
          * (represent the first 64 bits of the fractional parts of the
          * cube roots of the first sixty-four prime numbers)
          */
-		internal static readonly ulong[] K =
-		{
-			0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
-			0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
-			0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
-			0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
-			0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
-			0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
-			0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
-			0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
-			0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
-			0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
-			0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
-			0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
-			0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
-			0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
-			0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
-			0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
-			0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
-			0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
-			0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
-			0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
-		};
-
-		public int GetByteLength()
-		{
-			return MyByteLength;
-		}
-
-		public abstract string AlgorithmName { get; }
-		public abstract int GetDigestSize();
+        internal static readonly ulong[] K =
+        {
+            0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
+            0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
+            0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
+            0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
+            0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
+            0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
+            0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
+            0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
+            0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
+            0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
+            0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
+            0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
+            0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
+            0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
+            0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
+            0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
+            0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
+            0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
+            0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
+            0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
+        };
+
+        public int GetByteLength()
+        {
+            return MyByteLength;
+        }
+
+        public abstract string AlgorithmName { get; }
+        public abstract int GetDigestSize();
         public abstract int DoFinal(byte[] output, int outOff);
-		public abstract IMemoable Copy();
-		public abstract void Reset(IMemoable t);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public abstract int DoFinal(Span<byte> output);
+#endif
+        public abstract IMemoable Copy();
+        public abstract void Reset(IMemoable t);
     }
 }
diff --git a/crypto/src/crypto/digests/MD2Digest.cs b/crypto/src/crypto/digests/MD2Digest.cs
index f72d08768..cea89a311 100644
--- a/crypto/src/crypto/digests/MD2Digest.cs
+++ b/crypto/src/crypto/digests/MD2Digest.cs
@@ -1,6 +1,5 @@
 using System;
 
-using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
@@ -99,6 +98,30 @@ namespace Org.BouncyCastle.Crypto.Digests
             return DigestLength;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int DoFinal(Span<byte> output)
+        {
+            // add padding
+            byte paddingByte = (byte)(M.Length - mOff);
+            for (int i = mOff; i < M.Length; i++)
+            {
+                M[i] = paddingByte;
+            }
+            //do final check sum
+            ProcessChecksum(M);
+            // do final block process
+            ProcessBlock(M);
+
+            ProcessBlock(C);
+
+            X.AsSpan(xOff, 16).CopyTo(output);
+
+            Reset();
+
+            return DigestLength;
+        }
+#endif
+
         /**
         * reset the digest back to it's initial state.
         */
@@ -179,6 +202,40 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            //
+            // fill the current word
+            //
+            while ((mOff != 0) && (input.Length > 0))
+            {
+                Update(input[0]);
+                input = input[1..];
+            }
+
+            //
+            // process whole words.
+            //
+            while (input.Length >= 16)
+            {
+                input[..16].CopyTo(M);
+                ProcessChecksum(M);
+                ProcessBlock(M);
+                input = input[16..];
+            }
+
+            //
+            // load in the remainder.
+            //
+            while (input.Length > 0)
+            {
+                Update(input[0]);
+                input = input[1..];
+            }
+        }
+#endif
+
         internal void ProcessChecksum(byte[] m)
         {
             int L = C[15];
diff --git a/crypto/src/crypto/digests/MD4Digest.cs b/crypto/src/crypto/digests/MD4Digest.cs
index 8743f7dad..2eb2c8400 100644
--- a/crypto/src/crypto/digests/MD4Digest.cs
+++ b/crypto/src/crypto/digests/MD4Digest.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Crypto.Utilities;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
@@ -60,12 +61,9 @@ namespace Org.BouncyCastle.Crypto.Digests
 			return DigestLength;
 		}
 
-		internal override void ProcessWord(
-            byte[]  input,
-            int     inOff)
+		internal override void ProcessWord(byte[] input, int inOff)
         {
-            X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8)
-                | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24);
+            X[xOff++] = (int)Pack.LE_To_UInt32(input, inOff);
 
             if (xOff == 16)
             {
@@ -73,6 +71,18 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal override void ProcessWord(ReadOnlySpan<byte> word)
+        {
+            X[xOff++] = (int)Pack.LE_To_UInt32(word);
+
+            if (xOff == 16)
+            {
+                ProcessBlock();
+            }
+        }
+#endif
+
         internal override void ProcessLength(
             long    bitLength)
         {
@@ -85,32 +95,35 @@ namespace Org.BouncyCastle.Crypto.Digests
             X[15] = (int)((ulong) bitLength >> 32);
         }
 
-        private void UnpackWord(
-            int     word,
-            byte[]  outBytes,
-            int     outOff)
+        public override int DoFinal(byte[] output, int outOff)
         {
-            outBytes[outOff]     = (byte)word;
-            outBytes[outOff + 1] = (byte)((uint) word >> 8);
-            outBytes[outOff + 2] = (byte)((uint) word >> 16);
-            outBytes[outOff + 3] = (byte)((uint) word >> 24);
+            Finish();
+
+            Pack.UInt32_To_LE((uint)H1, output, outOff);
+            Pack.UInt32_To_LE((uint)H2, output, outOff + 4);
+            Pack.UInt32_To_LE((uint)H3, output, outOff + 8);
+            Pack.UInt32_To_LE((uint)H4, output, outOff + 12);
+
+            Reset();
+
+            return DigestLength;
         }
 
-        public override int DoFinal(
-            byte[]  output,
-            int     outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
         {
             Finish();
 
-            UnpackWord(H1, output, outOff);
-            UnpackWord(H2, output, outOff + 4);
-            UnpackWord(H3, output, outOff + 8);
-            UnpackWord(H4, output, outOff + 12);
+            Pack.UInt32_To_LE((uint)H1, output);
+            Pack.UInt32_To_LE((uint)H2, output[4..]);
+            Pack.UInt32_To_LE((uint)H3, output[8..]);
+            Pack.UInt32_To_LE((uint)H4, output[12..]);
 
             Reset();
 
             return DigestLength;
         }
+#endif
 
         /**
         * reset the chaining variables to the IV values.
diff --git a/crypto/src/crypto/digests/MD5Digest.cs b/crypto/src/crypto/digests/MD5Digest.cs
index c60ac92a3..062d7bb46 100644
--- a/crypto/src/crypto/digests/MD5Digest.cs
+++ b/crypto/src/crypto/digests/MD5Digest.cs
@@ -55,9 +55,7 @@ namespace Org.BouncyCastle.Crypto.Digests
             return DigestLength;
         }
 
-        internal override void ProcessWord(
-            byte[] input,
-            int inOff)
+        internal override void ProcessWord(byte[] input, int inOff)
         {
             X[xOff] = Pack.LE_To_UInt32(input, inOff);
 
@@ -67,6 +65,18 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal override void ProcessWord(ReadOnlySpan<byte> word)
+        {
+            X[xOff] = Pack.LE_To_UInt32(word);
+
+            if (++xOff == 16)
+            {
+                ProcessBlock();
+            }
+        }
+#endif
+
         internal override void ProcessLength(
             long bitLength)
         {
@@ -103,6 +113,22 @@ namespace Org.BouncyCastle.Crypto.Digests
             return DigestLength;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
+        {
+            Finish();
+
+            Pack.UInt32_To_LE(H1, output);
+            Pack.UInt32_To_LE(H2, output[4..]);
+            Pack.UInt32_To_LE(H3, output[8..]);
+            Pack.UInt32_To_LE(H4, output[12..]);
+
+            Reset();
+
+            return DigestLength;
+        }
+#endif
+
         /**
         * reset the chaining variables to the IV values.
         */
diff --git a/crypto/src/crypto/digests/NonMemoableDigest.cs b/crypto/src/crypto/digests/NonMemoableDigest.cs
index 02c49b887..bad38911b 100644
--- a/crypto/src/crypto/digests/NonMemoableDigest.cs
+++ b/crypto/src/crypto/digests/NonMemoableDigest.cs
@@ -44,11 +44,25 @@ namespace Org.BouncyCastle.Crypto.Digests
             mBaseDigest.BlockUpdate(input, inOff, len);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            mBaseDigest.BlockUpdate(input);
+        }
+#endif
+
         public virtual int DoFinal(byte[] output, int outOff)
         {
             return mBaseDigest.DoFinal(output, outOff);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int DoFinal(Span<byte> output)
+        {
+            return mBaseDigest.DoFinal(output);
+        }
+#endif
+
         public virtual void Reset()
         {
             mBaseDigest.Reset();
diff --git a/crypto/src/crypto/digests/NullDigest.cs b/crypto/src/crypto/digests/NullDigest.cs
index d14dd5c9f..28554cf3e 100644
--- a/crypto/src/crypto/digests/NullDigest.cs
+++ b/crypto/src/crypto/digests/NullDigest.cs
@@ -35,17 +35,48 @@ namespace Org.BouncyCastle.Crypto.Digests
 			bOut.Write(inBytes, inOff, len);
 		}
 
-        public int DoFinal(byte[] outBytes, int outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public void BlockUpdate(ReadOnlySpan<byte> input)
 		{
+			bOut.Write(input);
+		}
+#endif
+
+		public int DoFinal(byte[] outBytes, int outOff)
+		{
+            try
+            {
+                byte[] data = bOut.GetBuffer();
+				int length = Convert.ToInt32(bOut.Length);
+
+				Array.Copy(data, 0, outBytes, outOff, length);
+
+				return length;
+			}
+			finally
+            {
+                Reset();
+            }
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int DoFinal(Span<byte> output)
+        {
             try
             {
-                return Streams.WriteBufTo(bOut, outBytes, outOff);
+				byte[] data = bOut.GetBuffer();
+				int length = Convert.ToInt32(bOut.Length);
+
+				data.AsSpan(0, length).CopyTo(output);
+
+				return length;
             }
             finally
             {
                 Reset();
             }
         }
+#endif
 
         public void Reset()
 		{
diff --git a/crypto/src/crypto/digests/ParallelHash.cs b/crypto/src/crypto/digests/ParallelHash.cs
index f28795f5a..8054b2005 100644
--- a/crypto/src/crypto/digests/ParallelHash.cs
+++ b/crypto/src/crypto/digests/ParallelHash.cs
@@ -85,7 +85,7 @@ namespace Org.BouncyCastle.Crypto.Digests
             buffer[bufOff++] = b;
             if (bufOff == buffer.Length)
             {
-                compress();
+                Compress();
             }
         }
 
@@ -106,7 +106,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 
                 if (bufOff == buffer.Length)
                 {
-                    compress();
+                    Compress();
                 }
             }
 
@@ -114,7 +114,7 @@ namespace Org.BouncyCastle.Crypto.Digests
             {
                 while (len - i >= B)
                 {
-                    compress(inBuf, inOff + i, B);
+                    Compress(inBuf, inOff + i, B);
                     i += B;
                 }
             }
@@ -125,13 +125,49 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
-        private void compress()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
         {
-            compress(buffer, 0, bufOff);
+            //
+            // fill the current word
+            //
+            int i = 0;
+            if (bufOff != 0)
+            {
+                while (i < input.Length && bufOff != buffer.Length)
+                {
+                    buffer[bufOff++] = input[i++];
+                }
+
+                if (bufOff == buffer.Length)
+                {
+                    Compress();
+                }
+            }
+
+            if (i < input.Length)
+            {
+                while (input.Length - i >= B)
+                {
+                    Compress(input, i, B);
+                    i += B;
+                }
+            }
+
+            while (i < input.Length)
+            {
+                Update(input[i++]);
+            }
+        }
+#endif
+
+        private void Compress()
+        {
+            Compress(buffer, 0, bufOff);
             bufOff = 0;
         }
 
-        private void compress(byte[] buf, int offSet, int len)
+        private void Compress(byte[] buf, int offSet, int len)
         {
             compressor.BlockUpdate(buf, offSet, len);
             compressor.DoFinal(compressorBuffer, 0, compressorBuffer.Length);
@@ -141,11 +177,23 @@ namespace Org.BouncyCastle.Crypto.Digests
             nCount++;
         }
 
-        private void wrapUp(int outputSize)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void Compress(ReadOnlySpan<byte> input, int pos, int len)
+        {
+            compressor.BlockUpdate(input.Slice(pos, len));
+            compressor.DoFinal(compressorBuffer, 0, compressorBuffer.Length);
+
+            cshake.BlockUpdate(compressorBuffer, 0, compressorBuffer.Length);
+
+            nCount++;
+        }
+#endif
+
+        private void WrapUp(int outputSize)
         {
             if (bufOff != 0)
             {
-                compress();
+                Compress();
             }
             byte[] nOut = XofUtilities.RightEncode(nCount);
             byte[] encOut = XofUtilities.RightEncode(outputSize * 8);
@@ -160,21 +208,37 @@ namespace Org.BouncyCastle.Crypto.Digests
         {
             if (firstOutput)
             {
-                wrapUp(outputLength);
+                WrapUp(outputLength);
+            }
+
+            int rv = cshake.DoFinal(outBuf, outOff);
+
+            Reset();
+
+            return rv;
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int DoFinal(Span<byte> output)
+        {
+            if (firstOutput)
+            {
+                WrapUp(outputLength);
             }
 
-            int rv = cshake.DoFinal(outBuf, outOff, GetDigestSize());
+            int rv = cshake.DoFinal(output);
 
             Reset();
 
             return rv;
         }
+#endif
 
         public virtual int DoFinal(byte[] outBuf, int outOff, int outLen)
         {
             if (firstOutput)
             {
-                wrapUp(outputLength);
+                WrapUp(outputLength);
             }
 
             int rv = cshake.DoFinal(outBuf, outOff, outLen);
@@ -184,16 +248,44 @@ namespace Org.BouncyCastle.Crypto.Digests
             return rv;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int OutputFinal(Span<byte> output)
+        {
+            if (firstOutput)
+            {
+                WrapUp(outputLength);
+            }
+
+            int rv = cshake.OutputFinal(output);
+
+            Reset();
+
+            return rv;
+        }
+#endif
+
         public virtual int DoOutput(byte[] outBuf, int outOff, int outLen)
         {
             if (firstOutput)
             {
-                wrapUp(0);
+                WrapUp(0);
             }
 
             return cshake.DoOutput(outBuf, outOff, outLen);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int Output(Span<byte> output)
+        {
+            if (firstOutput)
+            {
+                WrapUp(0);
+            }
+
+            return cshake.Output(output);
+        }
+#endif
+
         public virtual void Reset()
         {
             cshake.Reset();
diff --git a/crypto/src/crypto/digests/RipeMD128Digest.cs b/crypto/src/crypto/digests/RipeMD128Digest.cs
index cba2c65d3..b66452682 100644
--- a/crypto/src/crypto/digests/RipeMD128Digest.cs
+++ b/crypto/src/crypto/digests/RipeMD128Digest.cs
@@ -68,6 +68,18 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal override void ProcessWord(ReadOnlySpan<byte> word)
+        {
+            X[xOff++] = (int)Pack.LE_To_UInt32(word);
+
+            if (xOff == 16)
+            {
+                ProcessBlock();
+            }
+        }
+#endif
+
         internal override void ProcessLength(
             long bitLength)
         {
@@ -94,6 +106,22 @@ namespace Org.BouncyCastle.Crypto.Digests
             return DigestLength;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
+        {
+            Finish();
+
+            Pack.UInt32_To_LE((uint)H0, output);
+            Pack.UInt32_To_LE((uint)H1, output[4..]);
+            Pack.UInt32_To_LE((uint)H2, output[8..]);
+            Pack.UInt32_To_LE((uint)H3, output[12..]);
+
+            Reset();
+
+            return DigestLength;
+        }
+#endif
+
         /**
         * reset the chaining variables to the IV values.
         */
diff --git a/crypto/src/crypto/digests/RipeMD160Digest.cs b/crypto/src/crypto/digests/RipeMD160Digest.cs
index 0fc2a4a1c..a95bff48a 100644
--- a/crypto/src/crypto/digests/RipeMD160Digest.cs
+++ b/crypto/src/crypto/digests/RipeMD160Digest.cs
@@ -70,6 +70,18 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal override void ProcessWord(ReadOnlySpan<byte> word)
+        {
+            X[xOff++] = (int)Pack.LE_To_UInt32(word);
+
+            if (xOff == 16)
+            {
+                ProcessBlock();
+            }
+        }
+#endif
+
         internal override void ProcessLength(
             long bitLength)
         {
@@ -97,6 +109,23 @@ namespace Org.BouncyCastle.Crypto.Digests
             return DigestLength;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
+        {
+            Finish();
+
+            Pack.UInt32_To_LE((uint)H0, output);
+            Pack.UInt32_To_LE((uint)H1, output[4..]);
+            Pack.UInt32_To_LE((uint)H2, output[8..]);
+            Pack.UInt32_To_LE((uint)H3, output[12..]);
+            Pack.UInt32_To_LE((uint)H4, output[16..]);
+
+            Reset();
+
+            return DigestLength;
+        }
+#endif
+
         /**
         * reset the chaining variables to the IV values.
         */
diff --git a/crypto/src/crypto/digests/RipeMD256Digest.cs b/crypto/src/crypto/digests/RipeMD256Digest.cs
index 621162a6f..40508e9f7 100644
--- a/crypto/src/crypto/digests/RipeMD256Digest.cs
+++ b/crypto/src/crypto/digests/RipeMD256Digest.cs
@@ -70,6 +70,18 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal override void ProcessWord(ReadOnlySpan<byte> word)
+        {
+            X[xOff++] = (int)Pack.LE_To_UInt32(word);
+
+            if (xOff == 16)
+            {
+                ProcessBlock();
+            }
+        }
+#endif
+
         internal override void ProcessLength(
             long bitLength)
         {
@@ -100,6 +112,26 @@ namespace Org.BouncyCastle.Crypto.Digests
             return DigestLength;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
+        {
+            Finish();
+
+            Pack.UInt32_To_LE((uint)H0, output);
+            Pack.UInt32_To_LE((uint)H1, output[4..]);
+            Pack.UInt32_To_LE((uint)H2, output[8..]);
+            Pack.UInt32_To_LE((uint)H3, output[12..]);
+            Pack.UInt32_To_LE((uint)H4, output[16..]);
+            Pack.UInt32_To_LE((uint)H5, output[20..]);
+            Pack.UInt32_To_LE((uint)H6, output[24..]);
+            Pack.UInt32_To_LE((uint)H7, output[28..]);
+
+            Reset();
+
+            return DigestLength;
+        }
+#endif
+
         /// <summary> reset the chaining variables to the IV values.</summary>
         public override void  Reset()
         {
diff --git a/crypto/src/crypto/digests/RipeMD320Digest.cs b/crypto/src/crypto/digests/RipeMD320Digest.cs
index c46bc4fea..ddaf858ff 100644
--- a/crypto/src/crypto/digests/RipeMD320Digest.cs
+++ b/crypto/src/crypto/digests/RipeMD320Digest.cs
@@ -73,6 +73,18 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal override void ProcessWord(ReadOnlySpan<byte> word)
+        {
+            X[xOff++] = (int)Pack.LE_To_UInt32(word);
+
+            if (xOff == 16)
+            {
+                ProcessBlock();
+            }
+        }
+#endif
+
         internal override void ProcessLength(
             long bitLength)
         {
@@ -105,6 +117,28 @@ namespace Org.BouncyCastle.Crypto.Digests
             return DigestLength;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
+        {
+            Finish();
+
+            Pack.UInt32_To_LE((uint)H0, output);
+            Pack.UInt32_To_LE((uint)H1, output[4..]);
+            Pack.UInt32_To_LE((uint)H2, output[8..]);
+            Pack.UInt32_To_LE((uint)H3, output[12..]);
+            Pack.UInt32_To_LE((uint)H4, output[16..]);
+            Pack.UInt32_To_LE((uint)H5, output[20..]);
+            Pack.UInt32_To_LE((uint)H6, output[24..]);
+            Pack.UInt32_To_LE((uint)H7, output[28..]);
+            Pack.UInt32_To_LE((uint)H8, output[32..]);
+            Pack.UInt32_To_LE((uint)H9, output[36..]);
+
+            Reset();
+
+            return DigestLength;
+        }
+#endif
+
         /// <summary> reset the chaining variables to the IV values.</summary>
         public override void  Reset()
         {
diff --git a/crypto/src/crypto/digests/SHA3Digest.cs b/crypto/src/crypto/digests/SHA3Digest.cs
index 3dc63a6ed..778c453d8 100644
--- a/crypto/src/crypto/digests/SHA3Digest.cs
+++ b/crypto/src/crypto/digests/SHA3Digest.cs
@@ -55,6 +55,15 @@ namespace Org.BouncyCastle.Crypto.Digests
             return base.DoFinal(output,  outOff);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
+        {
+            AbsorbBits(0x02, 2);
+
+            return base.DoFinal(output);
+        }
+#endif
+
         /*
          * TODO Possible API change to support partial-byte suffixes.
          */
diff --git a/crypto/src/crypto/digests/SM3Digest.cs b/crypto/src/crypto/digests/SM3Digest.cs
index 449d7c161..81d4a68a9 100644
--- a/crypto/src/crypto/digests/SM3Digest.cs
+++ b/crypto/src/crypto/digests/SM3Digest.cs
@@ -118,7 +118,6 @@ namespace Org.BouncyCastle.Crypto.Digests
 			this.xOff = 0;
 		}
 
-
 		public override int DoFinal(byte[] output, int outOff)
 		{
 			Finish();
@@ -130,13 +129,22 @@ namespace Org.BouncyCastle.Crypto.Digests
 			return DIGEST_LENGTH;
 		}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
+        {
+            Finish();
+
+            Pack.UInt32_To_BE(V, output);
+
+            Reset();
 
-		internal override void ProcessWord(byte[] input,
-		                                   int inOff)
+            return DIGEST_LENGTH;
+        }
+#endif
+
+        internal override void ProcessWord(byte[] input, int inOff)
 		{
-			uint n = Pack.BE_To_UInt32(input, inOff);
-			this.inwords[this.xOff] = n;
-			++this.xOff;
+			inwords[xOff++] = Pack.BE_To_UInt32(input, inOff);
 
 			if (this.xOff >= 16)
 			{
@@ -144,7 +152,19 @@ namespace Org.BouncyCastle.Crypto.Digests
 			}
 		}
 
-		internal override void ProcessLength(long bitLength)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal override void ProcessWord(ReadOnlySpan<byte> word)
+        {
+            inwords[xOff++] = Pack.BE_To_UInt32(word);
+
+            if (this.xOff >= 16)
+            {
+                ProcessBlock();
+            }
+        }
+#endif
+
+        internal override void ProcessLength(long bitLength)
 		{
 			if (this.xOff > (BLOCK_SIZE - 2))
 			{
diff --git a/crypto/src/crypto/digests/Sha1Digest.cs b/crypto/src/crypto/digests/Sha1Digest.cs
index 60ec651d5..9b384b8cb 100644
--- a/crypto/src/crypto/digests/Sha1Digest.cs
+++ b/crypto/src/crypto/digests/Sha1Digest.cs
@@ -61,9 +61,7 @@ namespace Org.BouncyCastle.Crypto.Digests
             return DigestLength;
         }
 
-        internal override void ProcessWord(
-            byte[]  input,
-            int     inOff)
+        internal override void ProcessWord(byte[] input, int inOff)
         {
             X[xOff] = Pack.BE_To_UInt32(input, inOff);
 
@@ -73,6 +71,18 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal override void ProcessWord(ReadOnlySpan<byte> word)
+        {
+            X[xOff] = Pack.BE_To_UInt32(word);
+
+            if (++xOff == 16)
+            {
+                ProcessBlock();
+            }
+        }
+#endif
+
         internal override void ProcessLength(long    bitLength)
         {
             if (xOff > 14)
@@ -101,6 +111,23 @@ namespace Org.BouncyCastle.Crypto.Digests
             return DigestLength;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
+        {
+            Finish();
+
+            Pack.UInt32_To_BE(H1, output);
+            Pack.UInt32_To_BE(H2, output[4..]);
+            Pack.UInt32_To_BE(H3, output[8..]);
+            Pack.UInt32_To_BE(H4, output[12..]);
+            Pack.UInt32_To_BE(H5, output[16..]);
+
+            Reset();
+
+            return DigestLength;
+        }
+#endif
+
         /**
          * reset the chaining variables
          */
@@ -279,6 +306,5 @@ namespace Org.BouncyCastle.Crypto.Digests
 
 			CopyIn(d);
 		}
-
     }
 }
diff --git a/crypto/src/crypto/digests/Sha224Digest.cs b/crypto/src/crypto/digests/Sha224Digest.cs
index b4e853745..28d09adec 100644
--- a/crypto/src/crypto/digests/Sha224Digest.cs
+++ b/crypto/src/crypto/digests/Sha224Digest.cs
@@ -72,9 +72,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 			return DigestLength;
 		}
 
-		internal override void ProcessWord(
-            byte[]  input,
-            int     inOff)
+		internal override void ProcessWord(byte[] input, int inOff)
         {
 			X[xOff] = Pack.BE_To_UInt32(input, inOff);
 
@@ -84,7 +82,19 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
-		internal override void ProcessLength(
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal override void ProcessWord(ReadOnlySpan<byte> word)
+        {
+            X[xOff] = Pack.BE_To_UInt32(word);
+
+            if (++xOff == 16)
+            {
+                ProcessBlock();
+            }
+        }
+#endif
+
+        internal override void ProcessLength(
             long bitLength)
         {
             if (xOff > 14)
@@ -96,9 +106,7 @@ namespace Org.BouncyCastle.Crypto.Digests
             X[15] = (uint)((ulong)bitLength);
         }
 
-        public override int DoFinal(
-            byte[]	output,
-            int		outOff)
+        public override int DoFinal(byte[] output, int outOff)
         {
             Finish();
 
@@ -115,7 +123,26 @@ namespace Org.BouncyCastle.Crypto.Digests
 			return DigestLength;
         }
 
-		/**
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
+        {
+            Finish();
+
+            Pack.UInt32_To_BE(H1, output);
+            Pack.UInt32_To_BE(H2, output[4..]);
+            Pack.UInt32_To_BE(H3, output[8..]);
+            Pack.UInt32_To_BE(H4, output[12..]);
+            Pack.UInt32_To_BE(H5, output[16..]);
+            Pack.UInt32_To_BE(H6, output[20..]);
+            Pack.UInt32_To_BE(H7, output[24..]);
+
+            Reset();
+
+            return DigestLength;
+        }
+#endif
+
+        /**
          * reset the chaining variables
          */
         public override void Reset()
@@ -284,6 +311,5 @@ namespace Org.BouncyCastle.Crypto.Digests
 
 			CopyIn(d);
 		}
-
     }
 }
diff --git a/crypto/src/crypto/digests/Sha256Digest.cs b/crypto/src/crypto/digests/Sha256Digest.cs
index 63d5b8bee..51859697e 100644
--- a/crypto/src/crypto/digests/Sha256Digest.cs
+++ b/crypto/src/crypto/digests/Sha256Digest.cs
@@ -67,9 +67,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 			return DigestLength;
 		}
 
-		internal override void ProcessWord(
-            byte[]  input,
-            int     inOff)
+		internal override void ProcessWord(byte[] input, int inOff)
 		{
 			X[xOff] = Pack.BE_To_UInt32(input, inOff);
 
@@ -79,7 +77,19 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
-		internal override void ProcessLength(
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal override void ProcessWord(ReadOnlySpan<byte> word)
+        {
+            X[xOff] = Pack.BE_To_UInt32(word);
+
+            if (++xOff == 16)
+            {
+                ProcessBlock();
+            }
+        }
+#endif
+
+        internal override void ProcessLength(
             long bitLength)
         {
             if (xOff > 14)
@@ -91,26 +101,44 @@ namespace Org.BouncyCastle.Crypto.Digests
             X[15] = (uint)((ulong)bitLength);
         }
 
-        public override int DoFinal(
-            byte[]  output,
-            int     outOff)
+        public override int DoFinal(byte[] output, int outOff)
         {
             Finish();
 
-            Pack.UInt32_To_BE((uint)H1, output, outOff);
-            Pack.UInt32_To_BE((uint)H2, output, outOff + 4);
-            Pack.UInt32_To_BE((uint)H3, output, outOff + 8);
-            Pack.UInt32_To_BE((uint)H4, output, outOff + 12);
-            Pack.UInt32_To_BE((uint)H5, output, outOff + 16);
-            Pack.UInt32_To_BE((uint)H6, output, outOff + 20);
-            Pack.UInt32_To_BE((uint)H7, output, outOff + 24);
-            Pack.UInt32_To_BE((uint)H8, output, outOff + 28);
+            Pack.UInt32_To_BE(H1, output, outOff);
+            Pack.UInt32_To_BE(H2, output, outOff + 4);
+            Pack.UInt32_To_BE(H3, output, outOff + 8);
+            Pack.UInt32_To_BE(H4, output, outOff + 12);
+            Pack.UInt32_To_BE(H5, output, outOff + 16);
+            Pack.UInt32_To_BE(H6, output, outOff + 20);
+            Pack.UInt32_To_BE(H7, output, outOff + 24);
+            Pack.UInt32_To_BE(H8, output, outOff + 28);
 
             Reset();
 
             return DigestLength;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
+        {
+            Finish();
+
+            Pack.UInt32_To_BE(H1, output);
+            Pack.UInt32_To_BE(H2, output[4..]);
+            Pack.UInt32_To_BE(H3, output[8..]);
+            Pack.UInt32_To_BE(H4, output[12..]);
+            Pack.UInt32_To_BE(H5, output[16..]);
+            Pack.UInt32_To_BE(H6, output[20..]);
+            Pack.UInt32_To_BE(H7, output[24..]);
+            Pack.UInt32_To_BE(H8, output[28..]);
+
+            Reset();
+
+            return DigestLength;
+        }
+#endif
+
         /**
         * reset the chaining variables
         */
@@ -313,6 +341,5 @@ namespace Org.BouncyCastle.Crypto.Digests
 
 			CopyIn(d);
 		}
-
     }
 }
diff --git a/crypto/src/crypto/digests/Sha384Digest.cs b/crypto/src/crypto/digests/Sha384Digest.cs
index e6c9a9aa9..e4e65ed85 100644
--- a/crypto/src/crypto/digests/Sha384Digest.cs
+++ b/crypto/src/crypto/digests/Sha384Digest.cs
@@ -64,6 +64,24 @@ namespace Org.BouncyCastle.Crypto.Digests
             return DigestLength;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
+        {
+            Finish();
+
+            Pack.UInt64_To_BE(H1, output);
+            Pack.UInt64_To_BE(H2, output[8..]);
+            Pack.UInt64_To_BE(H3, output[16..]);
+            Pack.UInt64_To_BE(H4, output[24..]);
+            Pack.UInt64_To_BE(H5, output[32..]);
+            Pack.UInt64_To_BE(H6, output[40..]);
+
+            Reset();
+
+            return DigestLength;
+        }
+#endif
+
         /**
         * reset the chaining variables
         */
@@ -72,9 +90,9 @@ namespace Org.BouncyCastle.Crypto.Digests
             base.Reset();
 
             /* SHA-384 initial hash value
-                * The first 64 bits of the fractional parts of the square roots
-                * of the 9th through 16th prime numbers
-                */
+             * The first 64 bits of the fractional parts of the square roots
+             * of the 9th through 16th prime numbers
+              */
             H1 = 0xcbbb9d5dc1059ed8;
             H2 = 0x629a292a367cd507;
             H3 = 0x9159015a3070dd17;
@@ -96,6 +114,5 @@ namespace Org.BouncyCastle.Crypto.Digests
 
 			CopyIn(d);
 		}
-
     }
 }
diff --git a/crypto/src/crypto/digests/Sha512Digest.cs b/crypto/src/crypto/digests/Sha512Digest.cs
index 2a0964fd3..9156c24bf 100644
--- a/crypto/src/crypto/digests/Sha512Digest.cs
+++ b/crypto/src/crypto/digests/Sha512Digest.cs
@@ -67,6 +67,26 @@ namespace Org.BouncyCastle.Crypto.Digests
 
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
+        {
+            Finish();
+
+            Pack.UInt64_To_BE(H1, output);
+            Pack.UInt64_To_BE(H2, output[8..]);
+            Pack.UInt64_To_BE(H3, output[16..]);
+            Pack.UInt64_To_BE(H4, output[24..]);
+            Pack.UInt64_To_BE(H5, output[32..]);
+            Pack.UInt64_To_BE(H6, output[40..]);
+            Pack.UInt64_To_BE(H7, output[48..]);
+            Pack.UInt64_To_BE(H8, output[56..]);
+
+            Reset();
+
+            return DigestLength;
+        }
+#endif
+
         /**
         * reset the chaining variables
         */
@@ -99,6 +119,5 @@ namespace Org.BouncyCastle.Crypto.Digests
 
 			CopyIn(d);
 		}
-
     }
 }
diff --git a/crypto/src/crypto/digests/Sha512tDigest.cs b/crypto/src/crypto/digests/Sha512tDigest.cs
index 2caefa763..939dbda4f 100644
--- a/crypto/src/crypto/digests/Sha512tDigest.cs
+++ b/crypto/src/crypto/digests/Sha512tDigest.cs
@@ -76,6 +76,26 @@ namespace Org.BouncyCastle.Crypto.Digests
             return digestLength;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
+        {
+            Finish();
+
+            UInt64_To_BE(H1, output, 0, digestLength);
+            UInt64_To_BE(H2, output, 8, digestLength - 8);
+            UInt64_To_BE(H3, output, 16, digestLength - 16);
+            UInt64_To_BE(H4, output, 24, digestLength - 24);
+            UInt64_To_BE(H5, output, 32, digestLength - 32);
+            UInt64_To_BE(H6, output, 40, digestLength - 40);
+            UInt64_To_BE(H7, output, 48, digestLength - 48);
+            UInt64_To_BE(H8, output, 56, digestLength - 56);
+
+            Reset();
+
+            return digestLength;
+        }
+#endif
+
         /**
          * reset the chaining variables
          */
@@ -170,7 +190,32 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
-		public override IMemoable Copy()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private static void UInt64_To_BE(ulong n, Span<byte> bs, int off, int max)
+        {
+            if (max > 0)
+            {
+                UInt32_To_BE((uint)(n >> 32), bs, off, max);
+
+                if (max > 4)
+                {
+                    UInt32_To_BE((uint)n, bs, off + 4, max - 4);
+                }
+            }
+        }
+
+        private static void UInt32_To_BE(uint n, Span<byte> bs, int off, int max)
+        {
+            int num = System.Math.Min(4, max);
+            while (--num >= 0)
+            {
+                int shift = 8 * (3 - num);
+                bs[off + num] = (byte)(n >> shift);
+            }
+        }
+#endif
+
+        public override IMemoable Copy()
 		{
 			return new Sha512tDigest(this);
 		}
diff --git a/crypto/src/crypto/digests/ShakeDigest.cs b/crypto/src/crypto/digests/ShakeDigest.cs
index 8d7a7d6e3..17d262261 100644
--- a/crypto/src/crypto/digests/ShakeDigest.cs
+++ b/crypto/src/crypto/digests/ShakeDigest.cs
@@ -77,6 +77,34 @@ namespace Org.BouncyCastle.Crypto.Digests
             return outLen;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
+        {
+            return OutputFinal(output[..GetDigestSize()]);
+        }
+
+        public virtual int OutputFinal(Span<byte> output)
+        {
+            int length = Output(output);
+
+            Reset();
+
+            return length;
+        }
+
+        public virtual int Output(Span<byte> output)
+        {
+            if (!squeezing)
+            {
+                AbsorbBits(0x0F, 4);
+            }
+
+            Squeeze(output);
+
+            return output.Length;
+        }
+#endif
+
         /*
          * TODO Possible API change to support partial-byte suffixes.
          */
diff --git a/crypto/src/crypto/digests/ShortenedDigest.cs b/crypto/src/crypto/digests/ShortenedDigest.cs
index 9e4d99e7b..9e9998560 100644
--- a/crypto/src/crypto/digests/ShortenedDigest.cs
+++ b/crypto/src/crypto/digests/ShortenedDigest.cs
@@ -1,5 +1,4 @@
 using System;
-using Org.BouncyCastle.Crypto;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
@@ -58,7 +57,14 @@ namespace Org.BouncyCastle.Crypto.Digests
 			baseDigest.BlockUpdate(input, inOff, length);
 		}
 
-		public int DoFinal(byte[] output, int outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            baseDigest.BlockUpdate(input);
+        }
+#endif
+
+        public int DoFinal(byte[] output, int outOff)
 		{
 			byte[] tmp = new byte[baseDigest.GetDigestSize()];
 
@@ -69,7 +75,20 @@ namespace Org.BouncyCastle.Crypto.Digests
 			return length;
 		}
 
-		public void Reset()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int DoFinal(Span<byte> output)
+        {
+            Span<byte> tmp = stackalloc byte[baseDigest.GetDigestSize()];
+
+            baseDigest.DoFinal(tmp);
+
+            tmp[..length].CopyTo(output);
+
+            return length;
+        }
+#endif
+
+        public void Reset()
 		{
 			baseDigest.Reset();
 		}
diff --git a/crypto/src/crypto/digests/SkeinDigest.cs b/crypto/src/crypto/digests/SkeinDigest.cs
index 394f0acd5..d56c0e788 100644
--- a/crypto/src/crypto/digests/SkeinDigest.cs
+++ b/crypto/src/crypto/digests/SkeinDigest.cs
@@ -5,7 +5,6 @@ using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
-
 	/// <summary>
 	/// Implementation of the Skein parameterised hash function in 256, 512 and 1024 bit block sizes,
 	/// based on the <see cref="Org.BouncyCastle.Crypto.Engines.ThreefishEngine">Threefish</see> tweakable block cipher.
@@ -103,7 +102,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 
 		public void BlockUpdate(byte[] inBytes, int inOff, int len)
 		{
-			engine.Update(inBytes, inOff, len);
+			engine.BlockUpdate(inBytes, inOff, len);
 		}
 
 		public int DoFinal(byte[] outBytes, int outOff)
@@ -111,5 +110,16 @@ namespace Org.BouncyCastle.Crypto.Digests
 			return engine.DoFinal(outBytes, outOff);
 		}
 
-	}
-}
\ No newline at end of file
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            engine.BlockUpdate(input);
+        }
+
+        public int DoFinal(Span<byte> output)
+        {
+            return engine.DoFinal(output);
+        }
+#endif
+    }
+}
diff --git a/crypto/src/crypto/digests/SkeinEngine.cs b/crypto/src/crypto/digests/SkeinEngine.cs
index a36ac8fe7..2535f786a 100644
--- a/crypto/src/crypto/digests/SkeinEngine.cs
+++ b/crypto/src/crypto/digests/SkeinEngine.cs
@@ -431,7 +431,7 @@ namespace Org.BouncyCastle.Crypto.Digests
                         currentOffset = 0;
                     }
 
-                    int toCopy = System.Math.Min((len - copied), currentBlock.Length - currentOffset);
+                    int toCopy = System.Math.Min(len - copied, currentBlock.Length - currentOffset);
                     Array.Copy(value, offset + copied, currentBlock, currentOffset, toCopy);
                     copied += toCopy;
                     currentOffset += toCopy;
@@ -439,6 +439,32 @@ namespace Org.BouncyCastle.Crypto.Digests
                 }
             }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public void Update(ReadOnlySpan<byte> input, ulong[] output)
+            {
+                /*
+                 * Buffer complete blocks for the underlying Threefish cipher, only flushing when there
+                 * are subsequent bytes (last block must be processed in doFinal() with final=true set).
+                 */
+                int copied = 0, len = input.Length;
+                while (len > copied)
+                {
+                    if (currentOffset == currentBlock.Length)
+                    {
+                        ProcessBlock(output);
+                        tweak.First = false;
+                        currentOffset = 0;
+                    }
+
+                    int toCopy = System.Math.Min(len - copied, currentBlock.Length - currentOffset);
+                    input.Slice(copied, toCopy).CopyTo(currentBlock.AsSpan(currentOffset));
+                    copied += toCopy;
+                    currentOffset += toCopy;
+                    tweak.AdvancePosition(toCopy);
+                }
+            }
+#endif
+
             private void ProcessBlock(ulong[] output)
             {
                 engine.threefish.Init(true, engine.chain, tweak.GetWords());
@@ -726,15 +752,23 @@ namespace Org.BouncyCastle.Crypto.Digests
         public void Update(byte inByte)
         {
             singleByte[0] = inByte;
-            Update(singleByte, 0, 1);
+            BlockUpdate(singleByte, 0, 1);
         }
 
-        public void Update(byte[] inBytes, int inOff, int len)
+        public void BlockUpdate(byte[] inBytes, int inOff, int len)
         {
             CheckInitialised();
             ubi.Update(inBytes, inOff, len, chain);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            CheckInitialised();
+            ubi.Update(input, chain);
+        }
+#endif
+
         public int DoFinal(byte[] outBytes, int outOff)
         {
             CheckInitialised();
@@ -770,6 +804,42 @@ namespace Org.BouncyCastle.Crypto.Digests
             return outputSizeBytes;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int DoFinal(Span<byte> output)
+        {
+            CheckInitialised();
+            if (output.Length < outputSizeBytes)
+                throw new DataLengthException("Output span is too short to hold output");
+
+            // Finalise message block
+            UbiFinal();
+
+            // Process additional post-message parameters
+            if (postMessageParameters != null)
+            {
+                for (int i = 0; i < postMessageParameters.Length; i++)
+                {
+                    Parameter param = postMessageParameters[i];
+                    UbiComplete(param.Type, param.Value);
+                }
+            }
+
+            // Perform the output transform
+            int blockSize = BlockSize;
+            int blocksRequired = (outputSizeBytes + blockSize - 1) / blockSize;
+            for (int i = 0; i < blocksRequired; i++)
+            {
+                int toWrite = System.Math.Min(blockSize, outputSizeBytes - (i * blockSize));
+                //Output((ulong)i, outBytes, outOff + (i * blockSize), toWrite);
+                Output((ulong)i, output[(i * blockSize)..], toWrite);
+            }
+
+            Reset();
+
+            return outputSizeBytes;
+        }
+#endif
+
         private void Output(ulong outputSequence, byte[] outBytes, int outOff, int outputBytes)
         {
             byte[] currentBytes = new byte[8];
@@ -796,5 +866,34 @@ namespace Org.BouncyCastle.Crypto.Digests
                 }
             }
         }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void Output(ulong outputSequence, Span<byte> output, int outputBytes)
+        {
+            Span<byte> currentBytes = stackalloc byte[8];
+            Pack.UInt64_To_LE(outputSequence, currentBytes);
+
+            // Output is a sequence of UBI invocations all of which use and preserve the pre-output state
+            ulong[] outputWords = new ulong[chain.Length];
+            UbiInit(PARAM_TYPE_OUTPUT);
+            this.ubi.Update(currentBytes, outputWords);
+            ubi.DoFinal(outputWords);
+
+            int wordsRequired = (outputBytes + 8 - 1) / 8;
+            for (int i = 0; i < wordsRequired; i++)
+            {
+                int toWrite = System.Math.Min(8, outputBytes - (i * 8));
+                if (toWrite == 8)
+                {
+                    Pack.UInt64_To_LE(outputWords[i], output[(i * 8)..]);
+                }
+                else
+                {
+                    Pack.UInt64_To_LE(outputWords[i], currentBytes);
+                    currentBytes[..toWrite].CopyTo(output[(i * 8)..]);
+                }
+            }
+        }
+#endif
     }
 }
diff --git a/crypto/src/crypto/digests/TigerDigest.cs b/crypto/src/crypto/digests/TigerDigest.cs
index a452d3f0b..d83e905db 100644
--- a/crypto/src/crypto/digests/TigerDigest.cs
+++ b/crypto/src/crypto/digests/TigerDigest.cs
@@ -603,6 +603,20 @@ namespace Org.BouncyCastle.Crypto.Digests
             bOff = 0;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void ProcessWord(ReadOnlySpan<byte> b)
+        {
+            x[xOff++] = (long)Pack.LE_To_UInt64(b);
+
+            if (xOff == x.Length)
+            {
+                ProcessBlock();
+            }
+
+            bOff = 0;
+        }
+#endif
+
         public void Update(
             byte input)
         {
@@ -656,6 +670,47 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            int inOff = 0, length = input.Length;
+
+            //
+            // fill the current word
+            //
+            while ((bOff != 0) && (length > 0))
+            {
+                Update(input[inOff]);
+
+                inOff++;
+                length--;
+            }
+
+            //
+            // process whole words.
+            //
+            while (length >= 8)
+            {
+                ProcessWord(input[inOff..]);
+
+                inOff += 8;
+                length -= 8;
+                byteCount += 8;
+            }
+
+            //
+            // load in the remainder.
+            //
+            while (length > 0)
+            {
+                Update(input[inOff]);
+
+                inOff++;
+                length--;
+            }
+        }
+#endif
+
         private void RoundABC(
             long    x,
             long    mul)
@@ -809,6 +864,21 @@ namespace Org.BouncyCastle.Crypto.Digests
             return DigestLength;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int DoFinal(Span<byte> output)
+        {
+            Finish();
+
+            Pack.UInt64_To_LE((ulong)a, output);
+            Pack.UInt64_To_LE((ulong)b, output[8..]);
+            Pack.UInt64_To_LE((ulong)c, output[16..]);
+
+            Reset();
+
+            return DigestLength;
+        }
+#endif
+
         /**
         * reset the chaining variables
         */
diff --git a/crypto/src/crypto/digests/TupleHash.cs b/crypto/src/crypto/digests/TupleHash.cs
index 98c2d2acf..43030d5d5 100644
--- a/crypto/src/crypto/digests/TupleHash.cs
+++ b/crypto/src/crypto/digests/TupleHash.cs
@@ -78,7 +78,7 @@ namespace Org.BouncyCastle.Crypto.Digests
             cshake.BlockUpdate(bytes, 0, bytes.Length);
         }
 
-        private void wrapUp(int outputSize)
+        private void WrapUp(int outputSize)
         {
             byte[] encOut = XofUtilities.RightEncode(outputSize * 8);
 
@@ -89,23 +89,14 @@ namespace Org.BouncyCastle.Crypto.Digests
 
         public virtual int DoFinal(byte[] outBuf, int outOff)
         {
-            if (firstOutput)
-            {
-                wrapUp(GetDigestSize());
-            }
-
-            int rv = cshake.DoFinal(outBuf, outOff, GetDigestSize());
-
-            Reset();
-
-            return rv;
+            return DoFinal(outBuf, outOff, GetDigestSize());
         }
 
         public virtual int DoFinal(byte[] outBuf, int outOff, int outLen)
         {
             if (firstOutput)
             {
-                wrapUp(GetDigestSize());
+                WrapUp(GetDigestSize());
             }
 
             int rv = cshake.DoFinal(outBuf, outOff, outLen);
@@ -119,7 +110,7 @@ namespace Org.BouncyCastle.Crypto.Digests
         {
             if (firstOutput)
             {
-                wrapUp(0);
+                WrapUp(0);
             }
 
             return cshake.DoOutput(outBuf, outOff, outLen);
@@ -130,5 +121,41 @@ namespace Org.BouncyCastle.Crypto.Digests
             cshake.Reset();
             firstOutput = true;
         }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            XofUtilities.EncodeTo(cshake, input);
+        }
+
+        public virtual int DoFinal(Span<byte> output)
+        {
+            return OutputFinal(output[..GetDigestSize()]);
+        }
+
+        public virtual int OutputFinal(Span<byte> output)
+        {
+            if (firstOutput)
+            {
+                WrapUp(GetDigestSize());
+            }
+
+            int rv = cshake.OutputFinal(output);
+
+            Reset();
+
+            return rv;
+        }
+
+        public virtual int Output(Span<byte> output)
+        {
+            if (firstOutput)
+            {
+                WrapUp(0);
+            }
+
+            return cshake.Output(output);
+        }
+#endif
     }
 }
diff --git a/crypto/src/crypto/digests/WhirlpoolDigest.cs b/crypto/src/crypto/digests/WhirlpoolDigest.cs
index b28e259f3..73d389a3c 100644
--- a/crypto/src/crypto/digests/WhirlpoolDigest.cs
+++ b/crypto/src/crypto/digests/WhirlpoolDigest.cs
@@ -162,6 +162,20 @@ namespace Org.BouncyCastle.Crypto.Digests
 			return GetDigestSize();
 		}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public int DoFinal(Span<byte> output)
+		{
+			// sets output[0..DIGEST_LENGTH_BYTES]
+			Finish();
+
+			Pack.UInt64_To_BE(_hash, output);
+
+			Reset();
+
+			return GetDigestSize();
+		}
+#endif
+
 		/**
 		* Reset the chaining variables
 		*/
@@ -276,6 +290,16 @@ namespace Org.BouncyCastle.Crypto.Digests
 			}
 		}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public void BlockUpdate(ReadOnlySpan<byte> input)
+		{
+			for (int i = 0; i < input.Length; ++i)
+			{
+				Update(input[i]);
+			}
+		}
+#endif
+
 		private void Finish()
 		{
 			/*
diff --git a/crypto/src/crypto/digests/XofUtils.cs b/crypto/src/crypto/digests/XofUtils.cs
index a4d6622b3..a1242f987 100644
--- a/crypto/src/crypto/digests/XofUtils.cs
+++ b/crypto/src/crypto/digests/XofUtils.cs
@@ -28,6 +28,26 @@ namespace Org.BouncyCastle.Crypto.Digests
             return b;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal static int LeftEncode(long length, Span<byte> lengthEncoding)
+        {
+            byte n = 1;
+
+            long v = length;
+            while ((v >>= 8) != 0)
+            {
+                n++;
+            }
+
+            lengthEncoding[0] = n;
+            for (int i = 1; i <= n; i++)
+            {
+                lengthEncoding[i] = (byte)(length >> (8 * (n - i)));
+            }
+            return 1 + n;
+        }
+#endif
+
         internal static byte[] RightEncode(long strLen)
         {
             byte n = 1;
@@ -50,6 +70,26 @@ namespace Org.BouncyCastle.Crypto.Digests
             return b;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal static int RightEncode(long length, Span<byte> lengthEncoding)
+        {
+            byte n = 1;
+
+            long v = length;
+            while ((v >>= 8) != 0)
+            {
+                n++;
+            }
+
+            lengthEncoding[n] = n;
+            for (int i = 0; i < n; i++)
+            {
+                lengthEncoding[i] = (byte)(length >> (8 * (n - i - 1)));
+            }
+            return n + 1;
+        }
+#endif
+
         internal static byte[] Encode(byte X)
         {
             return Arrays.Concatenate(LeftEncode(8), new byte[] { X });
@@ -63,5 +103,15 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
             return Arrays.Concatenate(LeftEncode(len * 8), Arrays.CopyOfRange(inBuf, inOff, inOff + len));
         }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal static void EncodeTo(IDigest digest, ReadOnlySpan<byte> buf)
+        {
+            Span<byte> lengthEncoding = stackalloc byte[9];
+            int count = LeftEncode(buf.Length * 8, lengthEncoding);
+            digest.BlockUpdate(lengthEncoding[..count]);
+            digest.BlockUpdate(buf);
+        }
+#endif
     }
 }
diff --git a/crypto/src/crypto/engines/AesEngine.cs b/crypto/src/crypto/engines/AesEngine.cs
index 10c720968..21daf06d8 100644
--- a/crypto/src/crypto/engines/AesEngine.cs
+++ b/crypto/src/crypto/engines/AesEngine.cs
@@ -486,6 +486,16 @@ namespace Org.BouncyCastle.Crypto.Engines
             Check.DataLength(input, inOff, 16, "input buffer too short");
             Check.OutputLength(output, outOff, 16, "output buffer too short");
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            if (forEncryption)
+            {
+                EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff), WorkingKey);
+            }
+            else
+            {
+                DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff), WorkingKey);
+            }
+#else
             if (forEncryption)
             {
                 EncryptBlock(input, inOff, output, outOff, WorkingKey);
@@ -494,14 +504,134 @@ namespace Org.BouncyCastle.Crypto.Engines
             {
                 DecryptBlock(input, inOff, output, outOff, WorkingKey);
             }
+#endif
 
             return BLOCK_SIZE;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            if (WorkingKey == null)
+                throw new InvalidOperationException("AES engine not initialised");
+
+            Check.DataLength(input, 16, "input buffer too short");
+            Check.OutputLength(output, 16, "output buffer too short");
+
+            if (forEncryption)
+            {
+                EncryptBlock(input, output, WorkingKey);
+            }
+            else
+            {
+                DecryptBlock(input, output, WorkingKey);
+            }
+
+            return BLOCK_SIZE;
+        }
+#endif
+
         public virtual void Reset()
         {
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output, uint[][] KW)
+        {
+            uint C0 = Pack.LE_To_UInt32(input);
+            uint C1 = Pack.LE_To_UInt32(input[4..]);
+            uint C2 = Pack.LE_To_UInt32(input[8..]);
+            uint C3 = Pack.LE_To_UInt32(input[12..]);
+
+            uint[] kw = KW[0];
+            uint t0 = C0 ^ kw[0];
+            uint t1 = C1 ^ kw[1];
+            uint t2 = C2 ^ kw[2];
+
+            uint r0, r1, r2, r3 = C3 ^ kw[3];
+            int r = 1;
+            while (r < ROUNDS - 1)
+            {
+                kw = KW[r++];
+                r0 = T0[t0 & 255] ^ Shift(T0[(t1 >> 8) & 255], 24) ^ Shift(T0[(t2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0];
+                r1 = T0[t1 & 255] ^ Shift(T0[(t2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(t0 >> 24) & 255], 8) ^ kw[1];
+                r2 = T0[t2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(t0 >> 16) & 255], 16) ^ Shift(T0[(t1 >> 24) & 255], 8) ^ kw[2];
+                r3 = T0[r3 & 255] ^ Shift(T0[(t0 >> 8) & 255], 24) ^ Shift(T0[(t1 >> 16) & 255], 16) ^ Shift(T0[(t2 >> 24) & 255], 8) ^ kw[3];
+                kw = KW[r++];
+                t0 = T0[r0 & 255] ^ Shift(T0[(r1 >> 8) & 255], 24) ^ Shift(T0[(r2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0];
+                t1 = T0[r1 & 255] ^ Shift(T0[(r2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(r0 >> 24) & 255], 8) ^ kw[1];
+                t2 = T0[r2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(r0 >> 16) & 255], 16) ^ Shift(T0[(r1 >> 24) & 255], 8) ^ kw[2];
+                r3 = T0[r3 & 255] ^ Shift(T0[(r0 >> 8) & 255], 24) ^ Shift(T0[(r1 >> 16) & 255], 16) ^ Shift(T0[(r2 >> 24) & 255], 8) ^ kw[3];
+            }
+
+            kw = KW[r++];
+            r0 = T0[t0 & 255] ^ Shift(T0[(t1 >> 8) & 255], 24) ^ Shift(T0[(t2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0];
+            r1 = T0[t1 & 255] ^ Shift(T0[(t2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(t0 >> 24) & 255], 8) ^ kw[1];
+            r2 = T0[t2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(t0 >> 16) & 255], 16) ^ Shift(T0[(t1 >> 24) & 255], 8) ^ kw[2];
+            r3 = T0[r3 & 255] ^ Shift(T0[(t0 >> 8) & 255], 24) ^ Shift(T0[(t1 >> 16) & 255], 16) ^ Shift(T0[(t2 >> 24) & 255], 8) ^ kw[3];
+
+            // the final round's table is a simple function of S so we don't use a whole other four tables for it
+
+            kw = KW[r];
+            C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)s[(r2 >> 16) & 255]) << 16) ^ (((uint)s[(r3 >> 24) & 255]) << 24) ^ kw[0];
+            C1 = (uint)s[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)s[(r0 >> 24) & 255]) << 24) ^ kw[1];
+            C2 = (uint)s[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24) ^ kw[2];
+            C3 = (uint)s[r3 & 255] ^ (((uint)s[(r0 >> 8) & 255]) << 8) ^ (((uint)s[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24) ^ kw[3];
+
+            Pack.UInt32_To_LE(C0, output);
+            Pack.UInt32_To_LE(C1, output[4..]);
+            Pack.UInt32_To_LE(C2, output[8..]);
+            Pack.UInt32_To_LE(C3, output[12..]);
+        }
+
+        private void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output, uint[][] KW)
+        {
+            uint C0 = Pack.LE_To_UInt32(input);
+            uint C1 = Pack.LE_To_UInt32(input[4..]);
+            uint C2 = Pack.LE_To_UInt32(input[8..]);
+            uint C3 = Pack.LE_To_UInt32(input[12..]);
+
+            uint[] kw = KW[ROUNDS];
+            uint t0 = C0 ^ kw[0];
+            uint t1 = C1 ^ kw[1];
+            uint t2 = C2 ^ kw[2];
+
+            uint r0, r1, r2, r3 = C3 ^ kw[3];
+            int r = ROUNDS - 1;
+            while (r > 1)
+            {
+                kw = KW[r--];
+                r0 = Tinv0[t0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(t2 >> 16) & 255], 16) ^ Shift(Tinv0[(t1 >> 24) & 255], 8) ^ kw[0];
+                r1 = Tinv0[t1 & 255] ^ Shift(Tinv0[(t0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(t2 >> 24) & 255], 8) ^ kw[1];
+                r2 = Tinv0[t2 & 255] ^ Shift(Tinv0[(t1 >> 8) & 255], 24) ^ Shift(Tinv0[(t0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2];
+                r3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(t2 >> 8) & 255], 24) ^ Shift(Tinv0[(t1 >> 16) & 255], 16) ^ Shift(Tinv0[(t0 >> 24) & 255], 8) ^ kw[3];
+                kw = KW[r--];
+                t0 = Tinv0[r0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(r2 >> 16) & 255], 16) ^ Shift(Tinv0[(r1 >> 24) & 255], 8) ^ kw[0];
+                t1 = Tinv0[r1 & 255] ^ Shift(Tinv0[(r0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(r2 >> 24) & 255], 8) ^ kw[1];
+                t2 = Tinv0[r2 & 255] ^ Shift(Tinv0[(r1 >> 8) & 255], 24) ^ Shift(Tinv0[(r0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2];
+                r3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(r2 >> 8) & 255], 24) ^ Shift(Tinv0[(r1 >> 16) & 255], 16) ^ Shift(Tinv0[(r0 >> 24) & 255], 8) ^ kw[3];
+            }
+
+            kw = KW[1];
+            r0 = Tinv0[t0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(t2 >> 16) & 255], 16) ^ Shift(Tinv0[(t1 >> 24) & 255], 8) ^ kw[0];
+            r1 = Tinv0[t1 & 255] ^ Shift(Tinv0[(t0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(t2 >> 24) & 255], 8) ^ kw[1];
+            r2 = Tinv0[t2 & 255] ^ Shift(Tinv0[(t1 >> 8) & 255], 24) ^ Shift(Tinv0[(t0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2];
+            r3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(t2 >> 8) & 255], 24) ^ Shift(Tinv0[(t1 >> 16) & 255], 16) ^ Shift(Tinv0[(t0 >> 24) & 255], 8) ^ kw[3];
+
+            // the final round's table is a simple function of Si so we don't use a whole other four tables for it
+
+            kw = KW[0];
+            C0 = (uint)Si[r0 & 255] ^ (((uint)s[(r3 >> 8) & 255]) << 8) ^ (((uint)s[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[(r1 >> 24) & 255]) << 24) ^ kw[0];
+            C1 = (uint)s[r1 & 255] ^ (((uint)s[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)s[(r2 >> 24) & 255]) << 24) ^ kw[1];
+            C2 = (uint)s[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)s[(r3 >> 24) & 255]) << 24) ^ kw[2];
+            C3 = (uint)Si[r3 & 255] ^ (((uint)s[(r2 >> 8) & 255]) << 8) ^ (((uint)s[(r1 >> 16) & 255]) << 16) ^ (((uint)s[(r0 >> 24) & 255]) << 24) ^ kw[3];
+
+            Pack.UInt32_To_LE(C0, output);
+            Pack.UInt32_To_LE(C1, output[4..]);
+            Pack.UInt32_To_LE(C2, output[8..]);
+            Pack.UInt32_To_LE(C3, output[12..]);
+        }
+#else
         private void EncryptBlock(byte[] input, int inOff, byte[] output, int outOff, uint[][] KW)
         {
             uint C0 = Pack.LE_To_UInt32(input, inOff +  0);
@@ -597,5 +727,6 @@ namespace Org.BouncyCastle.Crypto.Engines
             Pack.UInt32_To_LE(C2, output, outOff + 8);
             Pack.UInt32_To_LE(C3, output, outOff + 12);
         }
+#endif
     }
 }
diff --git a/crypto/src/crypto/engines/AesLightEngine.cs b/crypto/src/crypto/engines/AesLightEngine.cs
index 8d5a98a9f..f34901fac 100644
--- a/crypto/src/crypto/engines/AesLightEngine.cs
+++ b/crypto/src/crypto/engines/AesLightEngine.cs
@@ -380,6 +380,16 @@ namespace Org.BouncyCastle.Crypto.Engines
             Check.DataLength(input, inOff, 16, "input buffer too short");
             Check.OutputLength(output, outOff, 16, "output buffer too short");
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            if (forEncryption)
+            {
+                EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff), WorkingKey);
+            }
+            else
+            {
+                DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff), WorkingKey);
+            }
+#else
             if (forEncryption)
             {
                 EncryptBlock(input, inOff, output, outOff, WorkingKey);
@@ -388,14 +398,134 @@ namespace Org.BouncyCastle.Crypto.Engines
             {
                 DecryptBlock(input, inOff, output, outOff, WorkingKey);
             }
+#endif
 
             return BLOCK_SIZE;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            if (WorkingKey == null)
+                throw new InvalidOperationException("AES engine not initialised");
+
+            Check.DataLength(input, 16, "input buffer too short");
+            Check.OutputLength(output, 16, "output buffer too short");
+
+            if (forEncryption)
+            {
+                EncryptBlock(input, output, WorkingKey);
+            }
+            else
+            {
+                DecryptBlock(input, output, WorkingKey);
+            }
+
+            return BLOCK_SIZE;
+        }
+#endif
+
         public virtual void Reset()
         {
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output, uint[][] KW)
+        {
+            uint C0 = Pack.LE_To_UInt32(input);
+            uint C1 = Pack.LE_To_UInt32(input[4..]);
+            uint C2 = Pack.LE_To_UInt32(input[8..]);
+            uint C3 = Pack.LE_To_UInt32(input[12..]);
+
+            uint[] kw = KW[0];
+            uint t0 = C0 ^ kw[0];
+            uint t1 = C1 ^ kw[1];
+            uint t2 = C2 ^ kw[2];
+
+            uint r0, r1, r2, r3 = C3 ^ kw[3];
+            int r = 1;
+            while (r < ROUNDS - 1)
+            {
+                kw = KW[r++];
+                r0 = Mcol((uint)S[t0 & 255] ^ (((uint)S[(t1 >> 8) & 255]) << 8) ^ (((uint)S[(t2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24)) ^ kw[0];
+                r1 = Mcol((uint)S[t1 & 255] ^ (((uint)S[(t2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(t0 >> 24) & 255]) << 24)) ^ kw[1];
+                r2 = Mcol((uint)S[t2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(t0 >> 16) & 255]) << 16) ^ (((uint)S[(t1 >> 24) & 255]) << 24)) ^ kw[2];
+                r3 = Mcol((uint)S[r3 & 255] ^ (((uint)S[(t0 >> 8) & 255]) << 8) ^ (((uint)S[(t1 >> 16) & 255]) << 16) ^ (((uint)S[(t2 >> 24) & 255]) << 24)) ^ kw[3];
+                kw = KW[r++];
+                t0 = Mcol((uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24)) ^ kw[0];
+                t1 = Mcol((uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(r0 >> 24) & 255]) << 24)) ^ kw[1];
+                t2 = Mcol((uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24)) ^ kw[2];
+                r3 = Mcol((uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24)) ^ kw[3];
+            }
+
+            kw = KW[r++];
+            r0 = Mcol((uint)S[t0 & 255] ^ (((uint)S[(t1 >> 8) & 255]) << 8) ^ (((uint)S[(t2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24)) ^ kw[0];
+            r1 = Mcol((uint)S[t1 & 255] ^ (((uint)S[(t2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(t0 >> 24) & 255]) << 24)) ^ kw[1];
+            r2 = Mcol((uint)S[t2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(t0 >> 16) & 255]) << 16) ^ (((uint)S[(t1 >> 24) & 255]) << 24)) ^ kw[2];
+            r3 = Mcol((uint)S[r3 & 255] ^ (((uint)S[(t0 >> 8) & 255]) << 8) ^ (((uint)S[(t1 >> 16) & 255]) << 16) ^ (((uint)S[(t2 >> 24) & 255]) << 24)) ^ kw[3];
+
+            // the final round is a simple function of S
+
+            kw = KW[r];
+            C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24) ^ kw[0];
+            C1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(r0 >> 24) & 255]) << 24) ^ kw[1];
+            C2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24) ^ kw[2];
+            C3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24) ^ kw[3];
+
+            Pack.UInt32_To_LE(C0, output);
+            Pack.UInt32_To_LE(C1, output[4..]);
+            Pack.UInt32_To_LE(C2, output[8..]);
+            Pack.UInt32_To_LE(C3, output[12..]);
+        }
+
+        private void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output, uint[][] KW)
+        {
+            uint C0 = Pack.LE_To_UInt32(input);
+            uint C1 = Pack.LE_To_UInt32(input[4..]);
+            uint C2 = Pack.LE_To_UInt32(input[8..]);
+            uint C3 = Pack.LE_To_UInt32(input[12..]);
+
+            uint[] kw = KW[ROUNDS];
+            uint t0 = C0 ^ kw[0];
+            uint t1 = C1 ^ kw[1];
+            uint t2 = C2 ^ kw[2];
+
+            uint r0, r1, r2, r3 = C3 ^ kw[3];
+            int r = ROUNDS - 1;
+            while (r > 1)
+            {
+                kw = KW[r--];
+                r0 = Inv_Mcol((uint)Si[t0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(t2 >> 16) & 255]) << 16) ^ ((uint)Si[(t1 >> 24) & 255] << 24)) ^ kw[0];
+                r1 = Inv_Mcol((uint)Si[t1 & 255] ^ (((uint)Si[(t0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ ((uint)Si[(t2 >> 24) & 255] << 24)) ^ kw[1];
+                r2 = Inv_Mcol((uint)Si[t2 & 255] ^ (((uint)Si[(t1 >> 8) & 255]) << 8) ^ (((uint)Si[(t0 >> 16) & 255]) << 16) ^ ((uint)Si[(r3 >> 24) & 255] << 24)) ^ kw[2];
+                r3 = Inv_Mcol((uint)Si[r3 & 255] ^ (((uint)Si[(t2 >> 8) & 255]) << 8) ^ (((uint)Si[(t1 >> 16) & 255]) << 16) ^ ((uint)Si[(t0 >> 24) & 255] << 24)) ^ kw[3];
+                kw = KW[r--];
+                t0 = Inv_Mcol((uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ ((uint)Si[(r1 >> 24) & 255] << 24)) ^ kw[0];
+                t1 = Inv_Mcol((uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ ((uint)Si[(r2 >> 24) & 255] << 24)) ^ kw[1];
+                t2 = Inv_Mcol((uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ ((uint)Si[(r3 >> 24) & 255] << 24)) ^ kw[2];
+                r3 = Inv_Mcol((uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ ((uint)Si[(r0 >> 24) & 255] << 24)) ^ kw[3];
+            }
+
+            kw = KW[1];
+            r0 = Inv_Mcol((uint)Si[t0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(t2 >> 16) & 255]) << 16) ^ ((uint)Si[(t1 >> 24) & 255] << 24)) ^ kw[0];
+            r1 = Inv_Mcol((uint)Si[t1 & 255] ^ (((uint)Si[(t0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ ((uint)Si[(t2 >> 24) & 255] << 24)) ^ kw[1];
+            r2 = Inv_Mcol((uint)Si[t2 & 255] ^ (((uint)Si[(t1 >> 8) & 255]) << 8) ^ (((uint)Si[(t0 >> 16) & 255]) << 16) ^ ((uint)Si[(r3 >> 24) & 255] << 24)) ^ kw[2];
+            r3 = Inv_Mcol((uint)Si[r3 & 255] ^ (((uint)Si[(t2 >> 8) & 255]) << 8) ^ (((uint)Si[(t1 >> 16) & 255]) << 16) ^ ((uint)Si[(t0 >> 24) & 255] << 24)) ^ kw[3];
+
+            // the final round's table is a simple function of Si
+
+            kw = KW[0];
+            C0 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[(r1 >> 24) & 255]) << 24) ^ kw[0];
+            C1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[(r2 >> 24) & 255]) << 24) ^ kw[1];
+            C2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[(r3 >> 24) & 255]) << 24) ^ kw[2];
+            C3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[(r0 >> 24) & 255]) << 24) ^ kw[3];
+
+            Pack.UInt32_To_LE(C0, output);
+            Pack.UInt32_To_LE(C1, output[4..]);
+            Pack.UInt32_To_LE(C2, output[8..]);
+            Pack.UInt32_To_LE(C3, output[12..]);
+        }
+#else
         private void EncryptBlock(byte[] input, int inOff, byte[] output, int outOff, uint[][] KW)
         {
             uint C0 = Pack.LE_To_UInt32(input, inOff + 0);
@@ -491,5 +621,6 @@ namespace Org.BouncyCastle.Crypto.Engines
             Pack.UInt32_To_LE(C2, output, outOff + 8);
             Pack.UInt32_To_LE(C3, output, outOff + 12);
         }
+#endif
     }
 }
diff --git a/crypto/src/crypto/engines/AriaEngine.cs b/crypto/src/crypto/engines/AriaEngine.cs
index 2f94dc048..75af84320 100644
--- a/crypto/src/crypto/engines/AriaEngine.cs
+++ b/crypto/src/crypto/engines/AriaEngine.cs
@@ -195,6 +195,36 @@ namespace Org.BouncyCastle.Crypto.Engines
             return BlockSize;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            if (m_roundKeys == null)
+                throw new InvalidOperationException("ARIA engine not initialised");
+
+            Check.DataLength(input, BlockSize, "input buffer too short");
+            Check.OutputLength(output, BlockSize, "output buffer too short");
+
+            byte[] z = new byte[BlockSize];
+            input[..BlockSize].CopyTo(z);
+
+            int i = 0, rounds = m_roundKeys.Length - 3;
+            while (i < rounds)
+            {
+                FO(z, m_roundKeys[i++]);
+                FE(z, m_roundKeys[i++]);
+            }
+
+            FO(z, m_roundKeys[i++]);
+            Xor(z, m_roundKeys[i++]);
+            SL2(z);
+            Xor(z, m_roundKeys[i]);
+
+            z.CopyTo(output);
+
+            return BlockSize;
+        }
+#endif
+
         public virtual void Reset()
         {
             // Empty
diff --git a/crypto/src/crypto/engines/BlowfishEngine.cs b/crypto/src/crypto/engines/BlowfishEngine.cs
index 1b3dd9743..aa323581a 100644
--- a/crypto/src/crypto/engines/BlowfishEngine.cs
+++ b/crypto/src/crypto/engines/BlowfishEngine.cs
@@ -347,11 +347,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			get { return false; }
 		}
 
-		public  int ProcessBlock(
-            byte[]	input,
-            int		inOff,
-            byte[]	output,
-            int		outOff)
+		public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
             if (workingKey == null)
                 throw new InvalidOperationException("Blowfish not initialised");
@@ -359,7 +355,17 @@ namespace Org.BouncyCastle.Crypto.Engines
             Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
             Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
 
-            if (encrypting)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			if (encrypting)
+			{
+				EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+			}
+			else
+			{
+				DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+			}
+#else
+			if (encrypting)
             {
                 EncryptBlock(input, inOff, output, outOff);
             }
@@ -367,11 +373,34 @@ namespace Org.BouncyCastle.Crypto.Engines
             {
                 DecryptBlock(input, inOff, output, outOff);
             }
+#endif
 
             return BLOCK_SIZE;
         }
 
-        public void Reset()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			if (workingKey == null)
+				throw new InvalidOperationException("Blowfish not initialised");
+
+			Check.DataLength(input, BLOCK_SIZE, "input buffer too short");
+			Check.OutputLength(output, BLOCK_SIZE, "output buffer too short");
+
+			if (encrypting)
+			{
+				EncryptBlock(input, output);
+			}
+			else
+			{
+				DecryptBlock(input, output);
+			}
+
+			return BLOCK_SIZE;
+		}
+#endif
+
+		public void Reset()
         {
         }
 
@@ -499,16 +528,46 @@ namespace Org.BouncyCastle.Crypto.Engines
             ProcessTable(S2[SBOX_SK - 2], S2[SBOX_SK - 1], S3);
         }
 
-        /**
-        * Encrypt the given input starting at the given offset and place
-        * the result in the provided buffer starting at the given offset.
-        * The input will be an exact multiple of our blocksize.
-        */
-        private void EncryptBlock(
-            byte[]  src,
-            int     srcIndex,
-            byte[]  dst,
-            int     dstIndex)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		private void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			uint xl = Pack.BE_To_UInt32(input);
+			uint xr = Pack.BE_To_UInt32(input[4..]);
+
+			xl ^= P[0];
+
+			for (int i = 1; i < ROUNDS; i += 2)
+			{
+				xr ^= F(xl) ^ P[i];
+				xl ^= F(xr) ^ P[i + 1];
+			}
+
+			xr ^= P[ROUNDS + 1];
+
+			Pack.UInt32_To_BE(xr, output);
+			Pack.UInt32_To_BE(xl, output[4..]);
+		}
+
+		private void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			uint xl = Pack.BE_To_UInt32(input);
+			uint xr = Pack.BE_To_UInt32(input[4..]);
+
+			xl ^= P[ROUNDS + 1];
+
+			for (int i = ROUNDS; i > 0; i -= 2)
+			{
+				xr ^= F(xl) ^ P[i];
+				xl ^= F(xr) ^ P[i - 1];
+			}
+
+			xr ^= P[0];
+
+			Pack.UInt32_To_BE(xr, output);
+			Pack.UInt32_To_BE(xl, output[4..]);
+		}
+#else
+		private void EncryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex)
         {
             uint xl = Pack.BE_To_UInt32(src, srcIndex);
             uint xr = Pack.BE_To_UInt32(src, srcIndex+4);
@@ -527,16 +586,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             Pack.UInt32_To_BE(xl, dst, dstIndex + 4);
         }
 
-        /**
-        * Decrypt the given input starting at the given offset and place
-        * the result in the provided buffer starting at the given offset.
-        * The input will be an exact multiple of our blocksize.
-        */
-        private void DecryptBlock(
-            byte[] src,
-            int srcIndex,
-            byte[] dst,
-            int dstIndex)
+        private void DecryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex)
         {
             uint xl = Pack.BE_To_UInt32(src, srcIndex);
             uint xr = Pack.BE_To_UInt32(src, srcIndex + 4);
@@ -554,5 +604,6 @@ namespace Org.BouncyCastle.Crypto.Engines
             Pack.UInt32_To_BE(xr, dst, dstIndex);
             Pack.UInt32_To_BE(xl, dst, dstIndex + 4);
         }
-    }
+#endif
+	}
 }
diff --git a/crypto/src/crypto/engines/CamelliaEngine.cs b/crypto/src/crypto/engines/CamelliaEngine.cs
index 2222e4b7c..512448a27 100644
--- a/crypto/src/crypto/engines/CamelliaEngine.cs
+++ b/crypto/src/crypto/engines/CamelliaEngine.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Engines
 {
@@ -275,25 +276,6 @@ namespace Org.BouncyCastle.Crypto.Engines
 			ki[3 + ioff] = ko[1 + ooff];
 		}
 
-		private static uint bytes2uint(byte[] src, int offset)
-		{
-			uint word = 0;
-			for (int i = 0; i < 4; i++)
-			{
-				word = (word << 8) + (uint)src[i + offset];
-			}
-			return word;
-		}
-
-		private static void uint2bytes(uint word, byte[] dst, int offset)
-		{
-			for (int i = 0; i < 4; i++)
-			{
-				dst[(3 - i) + offset] = (byte)word;
-				word >>= 8;
-			}
-		}
-
 		private static void camelliaF2(uint[] s, uint[] skey, int keyoff)
 		{
 			uint t1, t2, u, v;
@@ -346,38 +328,23 @@ namespace Org.BouncyCastle.Crypto.Engines
 
 			switch (key.Length)
 			{
-				case 16:
-					_keyIs128 = true;
-					k[0] = bytes2uint(key, 0);
-					k[1] = bytes2uint(key, 4);
-					k[2] = bytes2uint(key, 8);
-					k[3] = bytes2uint(key, 12);
-					k[4] = k[5] = k[6] = k[7] = 0;
-					break;
-				case 24:
-					k[0] = bytes2uint(key, 0);
-					k[1] = bytes2uint(key, 4);
-					k[2] = bytes2uint(key, 8);
-					k[3] = bytes2uint(key, 12);
-					k[4] = bytes2uint(key, 16);
-					k[5] = bytes2uint(key, 20);
-					k[6] = ~k[4];
-					k[7] = ~k[5];
-					_keyIs128 = false;
-					break;
-				case 32:
-					k[0] = bytes2uint(key, 0);
-					k[1] = bytes2uint(key, 4);
-					k[2] = bytes2uint(key, 8);
-					k[3] = bytes2uint(key, 12);
-					k[4] = bytes2uint(key, 16);
-					k[5] = bytes2uint(key, 20);
-					k[6] = bytes2uint(key, 24);
-					k[7] = bytes2uint(key, 28);
-					_keyIs128 = false;
-					break;
-				default:
-					throw new ArgumentException("key sizes are only 16/24/32 bytes.");
+			case 16:
+				_keyIs128 = true;
+				Pack.BE_To_UInt32(key, 0, k, 0, 4);
+				k[4] = k[5] = k[6] = k[7] = 0;
+				break;
+			case 24:
+				Pack.BE_To_UInt32(key, 0, k, 0, 6);
+				k[6] = ~k[4];
+				k[7] = ~k[5];
+				_keyIs128 = false;
+				break;
+			case 32:
+				Pack.BE_To_UInt32(key, 0, k, 0, 8);
+				_keyIs128 = false;
+				break;
+			default:
+				throw new ArgumentException("key sizes are only 16/24/32 bytes.");
 			}
 
 			for (int i = 0; i < 4; i++)
@@ -537,13 +504,78 @@ namespace Org.BouncyCastle.Crypto.Engines
 			}
 		}
 
-		private int processBlock128(byte[] input, int inOff, byte[] output, int outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		private int ProcessBlock128(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			uint[] state = new uint[4];
+			Pack.BE_To_UInt32(input, state);
+
+			state[0] ^= kw[0];
+			state[1] ^= kw[1];
+			state[2] ^= kw[2];
+			state[3] ^= kw[3];
+
+			camelliaF2(state, subkey, 0);
+			camelliaF2(state, subkey, 4);
+			camelliaF2(state, subkey, 8);
+			camelliaFLs(state, ke, 0);
+			camelliaF2(state, subkey, 12);
+			camelliaF2(state, subkey, 16);
+			camelliaF2(state, subkey, 20);
+			camelliaFLs(state, ke, 4);
+			camelliaF2(state, subkey, 24);
+			camelliaF2(state, subkey, 28);
+			camelliaF2(state, subkey, 32);
+
+			Pack.UInt32_To_BE(state[2] ^ kw[4], output);
+			Pack.UInt32_To_BE(state[3] ^ kw[5], output[4..]);
+			Pack.UInt32_To_BE(state[0] ^ kw[6], output[8..]);
+			Pack.UInt32_To_BE(state[1] ^ kw[7], output[12..]);
+
+			return BLOCK_SIZE;
+		}
+
+		private int ProcessBlock192or256(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			uint[] state = new uint[4];
+			Pack.BE_To_UInt32(input, state);
+
+			state[0] ^= kw[0];
+			state[1] ^= kw[1];
+			state[2] ^= kw[2];
+			state[3] ^= kw[3];
+
+			camelliaF2(state, subkey, 0);
+			camelliaF2(state, subkey, 4);
+			camelliaF2(state, subkey, 8);
+			camelliaFLs(state, ke, 0);
+			camelliaF2(state, subkey, 12);
+			camelliaF2(state, subkey, 16);
+			camelliaF2(state, subkey, 20);
+			camelliaFLs(state, ke, 4);
+			camelliaF2(state, subkey, 24);
+			camelliaF2(state, subkey, 28);
+			camelliaF2(state, subkey, 32);
+			camelliaFLs(state, ke, 8);
+			camelliaF2(state, subkey, 36);
+			camelliaF2(state, subkey, 40);
+			camelliaF2(state, subkey, 44);
+
+			Pack.UInt32_To_BE(state[2] ^ kw[4], output);
+			Pack.UInt32_To_BE(state[3] ^ kw[5], output[4..]);
+			Pack.UInt32_To_BE(state[0] ^ kw[6], output[8..]);
+			Pack.UInt32_To_BE(state[1] ^ kw[7], output[12..]);
+
+			return BLOCK_SIZE;
+		}
+#else
+		private int ProcessBlock128(byte[] input, int inOff, byte[] output, int outOff)
 		{
 			uint[] state = new uint[4];
 
 			for (int i = 0; i < 4; i++)
 			{
-				state[i] = bytes2uint(input, inOff + (i * 4)) ^ kw[i];
+				state[i] = Pack.BE_To_UInt32(input, inOff + (i * 4)) ^ kw[i];
 			}
 
 			camelliaF2(state, subkey, 0);
@@ -558,26 +590,21 @@ namespace Org.BouncyCastle.Crypto.Engines
 			camelliaF2(state, subkey, 28);
 			camelliaF2(state, subkey, 32);
 
-			state[2] ^= kw[4];
-			state[3] ^= kw[5];
-			state[0] ^= kw[6];
-			state[1] ^= kw[7];
-
-			uint2bytes(state[2], output, outOff);
-			uint2bytes(state[3], output, outOff + 4);
-			uint2bytes(state[0], output, outOff + 8);
-			uint2bytes(state[1], output, outOff + 12);
+			Pack.UInt32_To_BE(state[2] ^ kw[4], output, outOff);
+			Pack.UInt32_To_BE(state[3] ^ kw[5], output, outOff + 4);
+			Pack.UInt32_To_BE(state[0] ^ kw[6], output, outOff + 8);
+			Pack.UInt32_To_BE(state[1] ^ kw[7], output, outOff + 12);
 
 			return BLOCK_SIZE;
 		}
 
-		private int processBlock192or256(byte[] input, int inOff, byte[] output, int outOff)
+		private int ProcessBlock192or256(byte[] input, int inOff, byte[] output, int outOff)
 		{
 			uint[] state = new uint[4];
 
 			for (int i = 0; i < 4; i++)
 			{
-				state[i] = bytes2uint(input, inOff + (i * 4)) ^ kw[i];
+				state[i] = Pack.BE_To_UInt32(input, inOff + (i * 4)) ^ kw[i];
 			}
 
 			camelliaF2(state, subkey, 0);
@@ -596,18 +623,14 @@ namespace Org.BouncyCastle.Crypto.Engines
 			camelliaF2(state, subkey, 40);
 			camelliaF2(state, subkey, 44);
 
-			state[2] ^= kw[4];
-			state[3] ^= kw[5];
-			state[0] ^= kw[6];
-			state[1] ^= kw[7];
-
-			uint2bytes(state[2], output, outOff);
-			uint2bytes(state[3], output, outOff + 4);
-			uint2bytes(state[0], output, outOff + 8);
-			uint2bytes(state[1], output, outOff + 12);
+			Pack.UInt32_To_BE(state[2] ^ kw[4], output, outOff);
+			Pack.UInt32_To_BE(state[3] ^ kw[5], output, outOff + 4);
+			Pack.UInt32_To_BE(state[0] ^ kw[6], output, outOff + 8);
+			Pack.UInt32_To_BE(state[1] ^ kw[7], output, outOff + 12);
 
 			return BLOCK_SIZE;
 		}
+#endif
 
 		public CamelliaEngine()
 		{
@@ -640,11 +663,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return BLOCK_SIZE;
 		}
 
-        public virtual int ProcessBlock(
-			byte[]	input,
-			int		inOff,
-			byte[]	output,
-			int		outOff)
+        public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
 		{
 			if (!initialised)
 				throw new InvalidOperationException("Camellia engine not initialised");
@@ -652,17 +671,48 @@ namespace Org.BouncyCastle.Crypto.Engines
             Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
             Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
 
-            if (_keyIs128)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			if (_keyIs128)
+			{
+				return ProcessBlock128(input.AsSpan(inOff), output.AsSpan(outOff));
+			}
+			else
+			{
+				return ProcessBlock192or256(input.AsSpan(inOff), output.AsSpan(outOff));
+			}
+#else
+			if (_keyIs128)
+			{
+				return ProcessBlock128(input, inOff, output, outOff);
+			}
+			else
+			{
+				return ProcessBlock192or256(input, inOff, output, outOff);
+			}
+#endif
+		}
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			if (!initialised)
+				throw new InvalidOperationException("Camellia engine not initialised");
+
+			Check.DataLength(input, BLOCK_SIZE, "input buffer too short");
+			Check.OutputLength(output, BLOCK_SIZE, "output buffer too short");
+
+			if (_keyIs128)
 			{
-				return processBlock128(input, inOff, output, outOff);
+				return ProcessBlock128(input, output);
 			}
 			else
 			{
-				return processBlock192or256(input, inOff, output, outOff);
+				return ProcessBlock192or256(input, output);
 			}
 		}
+#endif
 
-        public virtual void Reset()
+		public virtual void Reset()
 		{
 			// nothing
 		}
diff --git a/crypto/src/crypto/engines/CamelliaLightEngine.cs b/crypto/src/crypto/engines/CamelliaLightEngine.cs
index daf0316e2..03611f137 100644
--- a/crypto/src/crypto/engines/CamelliaLightEngine.cs
+++ b/crypto/src/crypto/engines/CamelliaLightEngine.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Engines
 {
@@ -158,25 +159,6 @@ namespace Org.BouncyCastle.Crypto.Engines
 			ki[3 + ioff] = ko[1 + ooff];
 		}
 
-		private static uint bytes2uint(byte[] src, int offset)
-		{
-			uint word = 0;
-			for (int i = 0; i < 4; i++)
-			{
-				word = (word << 8) + (uint)src[i + offset];
-			}
-			return word;
-		}
-
-		private static void uint2bytes(uint word, byte[] dst, int offset)
-		{
-			for (int i = 0; i < 4; i++)
-			{
-				dst[(3 - i) + offset] = (byte)word;
-				word >>= 8;
-			}
-		}
-
 		private byte lRot8(byte v, int rot)
 		{
 			return (byte)(((uint)v << rot) | ((uint)v >> (8 - rot)));
@@ -258,38 +240,23 @@ namespace Org.BouncyCastle.Crypto.Engines
 
 			switch (key.Length)
 			{
-				case 16:
-					_keyis128 = true;
-					k[0] = bytes2uint(key, 0);
-					k[1] = bytes2uint(key, 4);
-					k[2] = bytes2uint(key, 8);
-					k[3] = bytes2uint(key, 12);
-					k[4] = k[5] = k[6] = k[7] = 0;
-					break;
-				case 24:
-					k[0] = bytes2uint(key, 0);
-					k[1] = bytes2uint(key, 4);
-					k[2] = bytes2uint(key, 8);
-					k[3] = bytes2uint(key, 12);
-					k[4] = bytes2uint(key, 16);
-					k[5] = bytes2uint(key, 20);
-					k[6] = ~k[4];
-					k[7] = ~k[5];
-					_keyis128 = false;
-					break;
-				case 32:
-					k[0] = bytes2uint(key, 0);
-					k[1] = bytes2uint(key, 4);
-					k[2] = bytes2uint(key, 8);
-					k[3] = bytes2uint(key, 12);
-					k[4] = bytes2uint(key, 16);
-					k[5] = bytes2uint(key, 20);
-					k[6] = bytes2uint(key, 24);
-					k[7] = bytes2uint(key, 28);
-					_keyis128 = false;
-					break;
-				default:
-					throw new ArgumentException("key sizes are only 16/24/32 bytes.");
+			case 16:
+				_keyis128 = true;
+				Pack.BE_To_UInt32(key, 0, k, 0, 4);
+				k[4] = k[5] = k[6] = k[7] = 0;
+				break;
+			case 24:
+				Pack.BE_To_UInt32(key, 0, k, 0, 6);
+				k[6] = ~k[4];
+				k[7] = ~k[5];
+				_keyis128 = false;
+				break;
+			case 32:
+				Pack.BE_To_UInt32(key, 0, k, 0, 8);
+				_keyis128 = false;
+				break;
+			default:
+				throw new ArgumentException("key sizes are only 16/24/32 bytes.");
 			}
 
 			for (int i = 0; i < 4; i++)
@@ -449,13 +416,78 @@ namespace Org.BouncyCastle.Crypto.Engines
 			}
 		}
 
-		private int processBlock128(byte[] input, int inOff, byte[] output, int outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		private int ProcessBlock128(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			uint[] state = new uint[4];
+			Pack.BE_To_UInt32(input, state);
+
+			state[0] ^= kw[0];
+			state[1] ^= kw[1];
+			state[2] ^= kw[2];
+			state[3] ^= kw[3];
+
+			camelliaF2(state, subkey, 0);
+			camelliaF2(state, subkey, 4);
+			camelliaF2(state, subkey, 8);
+			camelliaFLs(state, ke, 0);
+			camelliaF2(state, subkey, 12);
+			camelliaF2(state, subkey, 16);
+			camelliaF2(state, subkey, 20);
+			camelliaFLs(state, ke, 4);
+			camelliaF2(state, subkey, 24);
+			camelliaF2(state, subkey, 28);
+			camelliaF2(state, subkey, 32);
+
+			Pack.UInt32_To_BE(state[2] ^ kw[4], output);
+			Pack.UInt32_To_BE(state[3] ^ kw[5], output[4..]);
+			Pack.UInt32_To_BE(state[0] ^ kw[6], output[8..]);
+			Pack.UInt32_To_BE(state[1] ^ kw[7], output[12..]);
+
+			return BLOCK_SIZE;
+		}
+
+		private int ProcessBlock192or256(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			uint[] state = new uint[4];
+			Pack.BE_To_UInt32(input, state);
+
+			state[0] ^= kw[0];
+			state[1] ^= kw[1];
+			state[2] ^= kw[2];
+			state[3] ^= kw[3];
+
+			camelliaF2(state, subkey, 0);
+			camelliaF2(state, subkey, 4);
+			camelliaF2(state, subkey, 8);
+			camelliaFLs(state, ke, 0);
+			camelliaF2(state, subkey, 12);
+			camelliaF2(state, subkey, 16);
+			camelliaF2(state, subkey, 20);
+			camelliaFLs(state, ke, 4);
+			camelliaF2(state, subkey, 24);
+			camelliaF2(state, subkey, 28);
+			camelliaF2(state, subkey, 32);
+			camelliaFLs(state, ke, 8);
+			camelliaF2(state, subkey, 36);
+			camelliaF2(state, subkey, 40);
+			camelliaF2(state, subkey, 44);
+
+			Pack.UInt32_To_BE(state[2] ^ kw[4], output);
+			Pack.UInt32_To_BE(state[3] ^ kw[5], output[4..]);
+			Pack.UInt32_To_BE(state[0] ^ kw[6], output[8..]);
+			Pack.UInt32_To_BE(state[1] ^ kw[7], output[12..]);
+
+			return BLOCK_SIZE;
+		}
+#else
+		private int ProcessBlock128(byte[] input, int inOff, byte[] output, int outOff)
 		{
 			uint[] state = new uint[4];
 
 			for (int i = 0; i < 4; i++)
 			{
-				state[i] = bytes2uint(input, inOff + (i * 4)) ^ kw[i];
+				state[i] = Pack.BE_To_UInt32(input, inOff + (i * 4)) ^ kw[i];
 			}
 
 			camelliaF2(state, subkey, 0);
@@ -470,26 +502,21 @@ namespace Org.BouncyCastle.Crypto.Engines
 			camelliaF2(state, subkey, 28);
 			camelliaF2(state, subkey, 32);
 
-			state[2] ^= kw[4];
-			state[3] ^= kw[5];
-			state[0] ^= kw[6];
-			state[1] ^= kw[7];
-
-			uint2bytes(state[2], output, outOff);
-			uint2bytes(state[3], output, outOff + 4);
-			uint2bytes(state[0], output, outOff + 8);
-			uint2bytes(state[1], output, outOff + 12);
+			Pack.UInt32_To_BE(state[2] ^ kw[4], output, outOff);
+			Pack.UInt32_To_BE(state[3] ^ kw[5], output, outOff + 4);
+			Pack.UInt32_To_BE(state[0] ^ kw[6], output, outOff + 8);
+			Pack.UInt32_To_BE(state[1] ^ kw[7], output, outOff + 12);
 
 			return BLOCK_SIZE;
 		}
 
-		private int processBlock192or256(byte[] input, int inOff, byte[] output, int outOff)
+		private int ProcessBlock192or256(byte[] input, int inOff, byte[] output, int outOff)
 		{
 			uint[] state = new uint[4];
 
 			for (int i = 0; i < 4; i++)
 			{
-				state[i] = bytes2uint(input, inOff + (i * 4)) ^ kw[i];
+				state[i] = Pack.BE_To_UInt32(input, inOff + (i * 4)) ^ kw[i];
 			}
 
 			camelliaF2(state, subkey, 0);
@@ -508,18 +535,14 @@ namespace Org.BouncyCastle.Crypto.Engines
 			camelliaF2(state, subkey, 40);
 			camelliaF2(state, subkey, 44);
 
-			state[2] ^= kw[4];
-			state[3] ^= kw[5];
-			state[0] ^= kw[6];
-			state[1] ^= kw[7];
-
-			uint2bytes(state[2], output, outOff);
-			uint2bytes(state[3], output, outOff + 4);
-			uint2bytes(state[0], output, outOff + 8);
-			uint2bytes(state[1], output, outOff + 12);
+			Pack.UInt32_To_BE(state[2] ^ kw[4], output, outOff);
+			Pack.UInt32_To_BE(state[3] ^ kw[5], output, outOff + 4);
+			Pack.UInt32_To_BE(state[0] ^ kw[6], output, outOff + 8);
+			Pack.UInt32_To_BE(state[1] ^ kw[7], output, outOff + 12);
 
 			return BLOCK_SIZE;
 		}
+#endif
 
 		public CamelliaLightEngine()
 		{
@@ -553,11 +576,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			initialised = true;
 		}
 
-        public virtual int ProcessBlock(
-			byte[]	input,
-			int		inOff,
-            byte[]	output,
-			int		outOff)
+        public virtual int ProcessBlock(byte[] input, int inOff, byte[]	output, int outOff)
 		{
 			if (!initialised)
 				throw new InvalidOperationException("Camellia engine not initialised");
@@ -565,17 +584,48 @@ namespace Org.BouncyCastle.Crypto.Engines
             Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
             Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
 
-            if (_keyis128)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			if (_keyis128)
+			{
+				return ProcessBlock128(input.AsSpan(inOff), output.AsSpan(outOff));
+			}
+			else
+			{
+				return ProcessBlock192or256(input.AsSpan(inOff), output.AsSpan(outOff));
+			}
+#else
+			if (_keyis128)
+			{
+				return ProcessBlock128(input, inOff, output, outOff);
+			}
+			else
+			{
+				return ProcessBlock192or256(input, inOff, output, outOff);
+			}
+#endif
+		}
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			if (!initialised)
+				throw new InvalidOperationException("Camellia engine not initialised");
+
+			Check.DataLength(input, BLOCK_SIZE, "input buffer too short");
+			Check.OutputLength(output, BLOCK_SIZE, "output buffer too short");
+
+			if (_keyis128)
 			{
-				return processBlock128(input, inOff, output, outOff);
+				return ProcessBlock128(input, output);
 			}
 			else
 			{
-				return processBlock192or256(input, inOff, output, outOff);
+				return ProcessBlock192or256(input, output);
 			}
 		}
+#endif
 
-        public virtual void Reset()
+		public virtual void Reset()
 		{
 		}
 	}
diff --git a/crypto/src/crypto/engines/Cast5Engine.cs b/crypto/src/crypto/engines/Cast5Engine.cs
index 398f6d43a..93288a237 100644
--- a/crypto/src/crypto/engines/Cast5Engine.cs
+++ b/crypto/src/crypto/engines/Cast5Engine.cs
@@ -352,19 +352,25 @@ namespace Org.BouncyCastle.Crypto.Engines
 			get { return false; }
 		}
 
-		public virtual int ProcessBlock(
-            byte[]	input,
-            int		inOff,
-            byte[]	output,
-            int		outOff)
+		public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
-			int blockSize = GetBlockSize();
             if (_workingKey == null)
                 throw new InvalidOperationException(AlgorithmName + " not initialised");
 
+            int blockSize = GetBlockSize();
             Check.DataLength(input, inOff, blockSize, "input buffer too short");
             Check.OutputLength(output, outOff, blockSize, "output buffer too short");
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            if (_encrypting)
+            {
+                return EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+            }
+            else
+            {
+                return DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+            }
+#else
             if (_encrypting)
             {
                 return EncryptBlock(input, inOff, output, outOff);
@@ -373,7 +379,29 @@ namespace Org.BouncyCastle.Crypto.Engines
             {
                 return DecryptBlock(input, inOff, output, outOff);
             }
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            if (_workingKey == null)
+                throw new InvalidOperationException(AlgorithmName + " not initialised");
+
+            int blockSize = GetBlockSize();
+            Check.DataLength(input, blockSize, "input buffer too short");
+            Check.OutputLength(output, blockSize, "output buffer too short");
+
+            if (_encrypting)
+            {
+                return EncryptBlock(input, output);
+            }
+            else
+            {
+                return DecryptBlock(input, output);
+            }
         }
+#endif
 
         public virtual void Reset()
         {
@@ -566,20 +594,45 @@ namespace Org.BouncyCastle.Crypto.Engines
             _Kr[16]=(int)((S5[x[0xE]]^S6[x[0xF]]^S7[x[0x1]]^S8[x[0x0]]^S8[x[0xD]])&0x1f);
         }
 
-        /**
-        * Encrypt the given input starting at the given offset and place
-        * the result in the provided buffer starting at the given offset.
-        *
-        * @param src        The plaintext buffer
-        * @param srcIndex    An offset into src
-        * @param dst        The ciphertext buffer
-        * @param dstIndex    An offset into dst
-        */
-        internal virtual int EncryptBlock(
-            byte[] src,
-            int srcIndex,
-            byte[] dst,
-            int dstIndex)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal virtual int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            // process the input block
+            // batch the units up into a 32 bit chunk and go for it
+            // the array is in bytes, the increment is 8x8 bits = 64
+
+            uint L0 = Pack.BE_To_UInt32(input);
+            uint R0 = Pack.BE_To_UInt32(input[4..]);
+
+            uint[] result = new uint[2];
+            CAST_Encipher(L0, R0, result);
+
+            // now stuff them into the destination block
+            Pack.UInt32_To_BE(result[0], output);
+            Pack.UInt32_To_BE(result[1], output[4..]);
+
+            return BLOCK_SIZE;
+        }
+
+        internal virtual int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            // process the input block
+            // batch the units up into a 32 bit chunk and go for it
+            // the array is in bytes, the increment is 8x8 bits = 64
+            uint L16 = Pack.BE_To_UInt32(input);
+            uint R16 = Pack.BE_To_UInt32(input[4..]);
+
+            uint[] result = new uint[2];
+            CAST_Decipher(L16, R16, result);
+
+            // now stuff them into the destination block
+            Pack.UInt32_To_BE(result[0], output);
+            Pack.UInt32_To_BE(result[1], output[4..]);
+
+            return BLOCK_SIZE;
+        }
+#else
+        internal virtual int EncryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex)
         {
             // process the input block
             // batch the units up into a 32 bit chunk and go for it
@@ -598,20 +651,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return BLOCK_SIZE;
         }
 
-        /**
-        * Decrypt the given input starting at the given offset and place
-        * the result in the provided buffer starting at the given offset.
-        *
-        * @param src        The plaintext buffer
-        * @param srcIndex    An offset into src
-        * @param dst        The ciphertext buffer
-        * @param dstIndex    An offset into dst
-        */
-        internal virtual int DecryptBlock(
-            byte[] src,
-            int srcIndex,
-            byte[] dst,
-            int dstIndex)
+        internal virtual int DecryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex)
         {
             // process the input block
             // batch the units up into a 32 bit chunk and go for it
@@ -628,6 +668,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 
             return BLOCK_SIZE;
         }
+#endif
 
         /**
         * The first of the three processing functions for the
@@ -702,28 +743,28 @@ namespace Org.BouncyCastle.Crypto.Engines
                 Li = Rp;
                 switch (i)
                 {
-                    case  1:
-                    case  4:
-                    case  7:
-                    case 10:
-                    case 13:
-                    case 16:
-                        Ri = Lp ^ F1(Rp, _Km[i], _Kr[i]);
-                        break;
-                    case  2:
-                    case  5:
-                    case  8:
-                    case 11:
-                    case 14:
-                        Ri = Lp ^ F2(Rp, _Km[i], _Kr[i]);
-                        break;
-                    case  3:
-                    case  6:
-                    case  9:
-                    case 12:
-                    case 15:
-                        Ri = Lp ^ F3(Rp, _Km[i], _Kr[i]);
-                        break;
+                case  1:
+                case  4:
+                case  7:
+                case 10:
+                case 13:
+                case 16:
+                    Ri = Lp ^ F1(Rp, _Km[i], _Kr[i]);
+                    break;
+                case  2:
+                case  5:
+                case  8:
+                case 11:
+                case 14:
+                    Ri = Lp ^ F2(Rp, _Km[i], _Kr[i]);
+                    break;
+                case  3:
+                case  6:
+                case  9:
+                case 12:
+                case 15:
+                    Ri = Lp ^ F3(Rp, _Km[i], _Kr[i]);
+                    break;
                 }
             }
 
@@ -752,28 +793,28 @@ namespace Org.BouncyCastle.Crypto.Engines
                 Li = Rp;
                 switch (i)
                 {
-                    case  1:
-                    case  4:
-                    case  7:
-                    case 10:
-                    case 13:
-                    case 16:
-                        Ri = Lp ^ F1(Rp, _Km[i], _Kr[i]);
-                        break;
-                    case  2:
-                    case  5:
-                    case  8:
-                    case 11:
-                    case 14:
-                        Ri = Lp ^ F2(Rp, _Km[i], _Kr[i]);
-                        break;
-                    case  3:
-                    case  6:
-                    case  9:
-                    case 12:
-                    case 15:
-                        Ri = Lp ^ F3(Rp, _Km[i], _Kr[i]);
-                        break;
+                case  1:
+                case  4:
+                case  7:
+                case 10:
+                case 13:
+                case 16:
+                    Ri = Lp ^ F1(Rp, _Km[i], _Kr[i]);
+                    break;
+                case  2:
+                case  5:
+                case  8:
+                case 11:
+                case 14:
+                    Ri = Lp ^ F2(Rp, _Km[i], _Kr[i]);
+                    break;
+                case  3:
+                case  6:
+                case  9:
+                case 12:
+                case 15:
+                    Ri = Lp ^ F3(Rp, _Km[i], _Kr[i]);
+                    break;
                 }
             }
 
diff --git a/crypto/src/crypto/engines/Cast6Engine.cs b/crypto/src/crypto/engines/Cast6Engine.cs
index c5c419b78..c3f379fcf 100644
--- a/crypto/src/crypto/engines/Cast6Engine.cs
+++ b/crypto/src/crypto/engines/Cast6Engine.cs
@@ -134,20 +134,44 @@ namespace Org.BouncyCastle.Crypto.Engines
             }
         }
 
-		/**
-        * Encrypt the given input starting at the given offset and place
-        * the result in the provided buffer starting at the given offset.
-        *
-        * @param src        The plaintext buffer
-        * @param srcIndex    An offset into src
-        * @param dst        The ciphertext buffer
-        * @param dstIndex    An offset into dst
-        */
-        internal override int EncryptBlock(
-            byte[]	src,
-            int		srcIndex,
-            byte[]	dst,
-            int		dstIndex)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal override int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            // process the input block
+            // batch the units up into 4x32 bit chunks and go for it
+            uint A = Pack.BE_To_UInt32(input);
+            uint B = Pack.BE_To_UInt32(input[4..]);
+            uint C = Pack.BE_To_UInt32(input[8..]);
+            uint D = Pack.BE_To_UInt32(input[12..]);
+            uint[] result = new uint[4];
+            CAST_Encipher(A, B, C, D, result);
+            // now stuff them into the destination block
+            Pack.UInt32_To_BE(result[0], output);
+            Pack.UInt32_To_BE(result[1], output[4..]);
+            Pack.UInt32_To_BE(result[2], output[8..]);
+            Pack.UInt32_To_BE(result[3], output[12..]);
+            return BLOCK_SIZE;
+        }
+
+        internal override int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            // process the input block
+            // batch the units up into 4x32 bit chunks and go for it
+            uint A = Pack.BE_To_UInt32(input);
+            uint B = Pack.BE_To_UInt32(input[4..]);
+            uint C = Pack.BE_To_UInt32(input[8..]);
+            uint D = Pack.BE_To_UInt32(input[12..]);
+            uint[] result = new uint[4];
+            CAST_Decipher(A, B, C, D, result);
+            // now stuff them into the destination block
+            Pack.UInt32_To_BE(result[0], output);
+            Pack.UInt32_To_BE(result[1], output[4..]);
+            Pack.UInt32_To_BE(result[2], output[8..]);
+            Pack.UInt32_To_BE(result[3], output[12..]);
+            return BLOCK_SIZE;
+        }
+#else
+        internal override int EncryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex)
         {
             // process the input block
             // batch the units up into 4x32 bit chunks and go for it
@@ -165,20 +189,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return BLOCK_SIZE;
         }
 
-		/**
-        * Decrypt the given input starting at the given offset and place
-        * the result in the provided buffer starting at the given offset.
-        *
-        * @param src        The plaintext buffer
-        * @param srcIndex    An offset into src
-        * @param dst        The ciphertext buffer
-        * @param dstIndex    An offset into dst
-        */
-        internal override int DecryptBlock(
-            byte[]	src,
-            int		srcIndex,
-            byte[]	dst,
-            int		dstIndex)
+        internal override int DecryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex)
         {
             // process the input block
             // batch the units up into 4x32 bit chunks and go for it
@@ -195,8 +206,9 @@ namespace Org.BouncyCastle.Crypto.Engines
             Pack.UInt32_To_BE(result[3], dst, dstIndex + 12);
             return BLOCK_SIZE;
         }
+#endif
 
-		/**
+        /**
         * Does the 12 quad rounds rounds to encrypt the block.
         *
         * @param A    the 00-31  bits of the plaintext block
diff --git a/crypto/src/crypto/engines/DesEdeEngine.cs b/crypto/src/crypto/engines/DesEdeEngine.cs
index 2fac24ac0..ffb18d753 100644
--- a/crypto/src/crypto/engines/DesEdeEngine.cs
+++ b/crypto/src/crypto/engines/DesEdeEngine.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Engines
@@ -63,11 +64,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return BLOCK_SIZE;
         }
 
-        public override int ProcessBlock(
-            byte[]	input,
-            int		inOff,
-            byte[]	output,
-            int		outOff)
+        public override int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
             if (workingKey1 == null)
                 throw new InvalidOperationException("DESede engine not initialised");
@@ -75,23 +72,59 @@ namespace Org.BouncyCastle.Crypto.Engines
             Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
             Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
 
-            byte[] temp = new byte[BLOCK_SIZE];
+            uint hi32 = Pack.BE_To_UInt32(input, inOff);
+            uint lo32 = Pack.BE_To_UInt32(input, inOff + 4);
 
             if (forEncryption)
             {
-                DesFunc(workingKey1, input, inOff, temp, 0);
-                DesFunc(workingKey2, temp, 0, temp, 0);
-                DesFunc(workingKey3, temp, 0, output, outOff);
+                DesFunc(workingKey1, ref hi32, ref lo32);
+                DesFunc(workingKey2, ref hi32, ref lo32);
+                DesFunc(workingKey3, ref hi32, ref lo32);
             }
             else
             {
-                DesFunc(workingKey3, input, inOff, temp, 0);
-                DesFunc(workingKey2, temp, 0, temp, 0);
-                DesFunc(workingKey1, temp, 0, output, outOff);
+                DesFunc(workingKey3, ref hi32, ref lo32);
+                DesFunc(workingKey2, ref hi32, ref lo32);
+                DesFunc(workingKey1, ref hi32, ref lo32);
             }
 
+            Pack.UInt32_To_BE(hi32, output, outOff);
+            Pack.UInt32_To_BE(lo32, output, outOff + 4);
+
+            return BLOCK_SIZE;
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            if (workingKey1 == null)
+                throw new InvalidOperationException("DESede engine not initialised");
+
+            Check.DataLength(input, BLOCK_SIZE, "input buffer too short");
+            Check.OutputLength(output, BLOCK_SIZE, "output buffer too short");
+
+            uint hi32 = Pack.BE_To_UInt32(input);
+            uint lo32 = Pack.BE_To_UInt32(input[4..]);
+
+            if (forEncryption)
+            {
+                DesFunc(workingKey1, ref hi32, ref lo32);
+                DesFunc(workingKey2, ref hi32, ref lo32);
+                DesFunc(workingKey3, ref hi32, ref lo32);
+            }
+            else
+            {
+                DesFunc(workingKey3, ref hi32, ref lo32);
+                DesFunc(workingKey2, ref hi32, ref lo32);
+                DesFunc(workingKey1, ref hi32, ref lo32);
+            }
+
+            Pack.UInt32_To_BE(hi32, output);
+            Pack.UInt32_To_BE(lo32, output[4..]);
+
             return BLOCK_SIZE;
         }
+#endif
 
         public override void Reset()
         {
diff --git a/crypto/src/crypto/engines/DesEngine.cs b/crypto/src/crypto/engines/DesEngine.cs
index cfd50681e..25f559652 100644
--- a/crypto/src/crypto/engines/DesEngine.cs
+++ b/crypto/src/crypto/engines/DesEngine.cs
@@ -31,10 +31,10 @@ namespace Org.BouncyCastle.Crypto.Engines
             bool				forEncryption,
             ICipherParameters	parameters)
         {
-            if (!(parameters is KeyParameter))
+            if (!(parameters is KeyParameter keyParameter))
 				throw new ArgumentException("invalid parameter passed to DES init - " + Platform.GetTypeName(parameters));
 
-			workingKey = GenerateWorkingKey(forEncryption, ((KeyParameter)parameters).GetKey());
+			workingKey = GenerateWorkingKey(forEncryption, keyParameter.GetKey());
         }
 
 		public virtual string AlgorithmName
@@ -52,11 +52,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return BLOCK_SIZE;
         }
 
-        public virtual int ProcessBlock(
-            byte[]	input,
-            int		inOff,
-            byte[]	output,
-            int		outOff)
+        public virtual int ProcessBlock(byte[] input, int inOff, byte[]	output, int outOff)
         {
             if (workingKey == null)
                 throw new InvalidOperationException("DES engine not initialised");
@@ -64,11 +60,38 @@ namespace Org.BouncyCastle.Crypto.Engines
             Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
             Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
 
-            DesFunc(workingKey, input, inOff, output, outOff);
+            uint hi32 = Pack.BE_To_UInt32(input, inOff);
+            uint lo32 = Pack.BE_To_UInt32(input, inOff + 4);
+
+            DesFunc(workingKey, ref hi32, ref lo32);
+
+            Pack.UInt32_To_BE(hi32, output, outOff);
+            Pack.UInt32_To_BE(lo32, output, outOff + 4);
 
 			return BLOCK_SIZE;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            if (workingKey == null)
+                throw new InvalidOperationException("DES engine not initialised");
+
+            Check.DataLength(input, BLOCK_SIZE, "input buffer too short");
+            Check.OutputLength(output, BLOCK_SIZE, "output buffer too short");
+
+            uint hi32 = Pack.BE_To_UInt32(input);
+            uint lo32 = Pack.BE_To_UInt32(input[4..]);
+
+            DesFunc(workingKey, ref hi32, ref lo32);
+
+            Pack.UInt32_To_BE(hi32, output);
+            Pack.UInt32_To_BE(lo32, output[4..]);
+
+            return BLOCK_SIZE;
+        }
+#endif
+
         public virtual void Reset()
         {
         }
@@ -388,19 +411,11 @@ namespace Org.BouncyCastle.Crypto.Engines
             return newKey;
         }
 
-        /**
-        * the DES engine.
-        */
-        internal static void DesFunc(
-            int[]	wKey,
-            byte[]	input,
-            int		inOff,
-            byte[]	outBytes,
-            int		outOff)
+        internal static void DesFunc(int[] wKey, ref uint hi32, ref uint lo32)
         {
-			uint left = Pack.BE_To_UInt32(input, inOff);
-			uint right = Pack.BE_To_UInt32(input, inOff + 4);
-			uint work;
+            uint left = hi32;
+            uint right = lo32;
+            uint work;
 
             work = ((left >> 4) ^ right) & 0x0f0f0f0f;
             right ^= work;
@@ -468,8 +483,8 @@ namespace Org.BouncyCastle.Crypto.Engines
             left ^= work;
             right ^= (work << 4);
 
-			Pack.UInt32_To_BE(right, outBytes, outOff);
-			Pack.UInt32_To_BE(left, outBytes, outOff + 4);
+            hi32 = right;
+            lo32 = left;
         }
     }
 }
diff --git a/crypto/src/crypto/engines/Dstu7624Engine.cs b/crypto/src/crypto/engines/Dstu7624Engine.cs
index 844a873a8..a0ff8ebd4 100644
--- a/crypto/src/crypto/engines/Dstu7624Engine.cs
+++ b/crypto/src/crypto/engines/Dstu7624Engine.cs
@@ -268,7 +268,11 @@ namespace Org.BouncyCastle.Crypto.Engines
                 {
                 case 2:
                 {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                    EncryptBlock_128(input.AsSpan(inOff), output.AsSpan(outOff));
+#else
                     EncryptBlock_128(input, inOff, output, outOff);
+#endif
                     break;
                 }
                 default:
@@ -299,7 +303,11 @@ namespace Org.BouncyCastle.Crypto.Engines
                 {
                 case 2:
                 {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                    DecryptBlock_128(input.AsSpan(inOff), output.AsSpan(outOff));
+#else
                     DecryptBlock_128(input, inOff, output, outOff);
+#endif
                     break;
                 }
                 default:
@@ -327,6 +335,82 @@ namespace Org.BouncyCastle.Crypto.Engines
             return GetBlockSize();
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            if (workingKey == null)
+                throw new InvalidOperationException("Dstu7624Engine not initialised");
+
+            Check.DataLength(input, GetBlockSize(), "input buffer too short");
+            Check.OutputLength(output, GetBlockSize(), "output buffer too short");
+
+            if (forEncryption)
+            {
+                /* Encrypt */
+                switch (wordsInBlock)
+                {
+                case 2:
+                {
+                    EncryptBlock_128(input, output);
+                    break;
+                }
+                default:
+                {
+                    Pack.LE_To_UInt64(input, internalState);
+                    AddRoundKey(0);
+                    for (int round = 0;;)
+                    {
+                        EncryptionRound();
+
+                        if (++round == roundsAmount)
+                        {
+                            break;
+                        }
+
+                        XorRoundKey(round);
+                    }
+                    AddRoundKey(roundsAmount);
+                    Pack.UInt64_To_LE(internalState, output);
+                    break;
+                }
+                }
+            }
+            else
+            {
+                /* Decrypt */
+                switch (wordsInBlock)
+                {
+                case 2:
+                {
+                    DecryptBlock_128(input, output);
+                    break;
+                }
+                default:
+                {
+                    Pack.LE_To_UInt64(input, internalState);
+                    SubRoundKey(roundsAmount);
+                    for (int round = roundsAmount;;)
+                    {
+                        DecryptionRound();
+
+                        if (--round == 0)
+                        {
+                            break;
+                        }
+    
+                        XorRoundKey(round);
+                    }
+                    SubRoundKey(0);
+                    Pack.UInt64_To_LE(internalState, output);
+                    break;
+                }
+                }
+            }
+
+            return GetBlockSize();
+        }
+#endif
+
         private void EncryptionRound()
         {
             SubBytes();
@@ -341,6 +425,133 @@ namespace Org.BouncyCastle.Crypto.Engines
             InvSubBytes();
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void DecryptBlock_128(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            ulong c0 = Pack.LE_To_UInt64(input);
+            ulong c1 = Pack.LE_To_UInt64(input[8..]);
+
+            ulong[] roundKey = roundKeys[roundsAmount];
+            c0 -= roundKey[0];
+            c1 -= roundKey[1];
+
+            for (int round = roundsAmount; ;)
+            {
+                c0 = MixColumnInv(c0);
+                c1 = MixColumnInv(c1);
+
+                uint lo0 = (uint)c0, hi0 = (uint)(c0 >> 32);
+                uint lo1 = (uint)c1, hi1 = (uint)(c1 >> 32);
+
+                {
+                    byte t0 = T0[lo0 & 0xFF];
+                    byte t1 = T1[(lo0 >> 8) & 0xFF];
+                    byte t2 = T2[(lo0 >> 16) & 0xFF];
+                    byte t3 = T3[lo0 >> 24];
+                    lo0 = (uint)t0 | ((uint)t1 << 8) | ((uint)t2 << 16) | ((uint)t3 << 24);
+                    byte t4 = T0[hi1 & 0xFF];
+                    byte t5 = T1[(hi1 >> 8) & 0xFF];
+                    byte t6 = T2[(hi1 >> 16) & 0xFF];
+                    byte t7 = T3[hi1 >> 24];
+                    hi1 = (uint)t4 | ((uint)t5 << 8) | ((uint)t6 << 16) | ((uint)t7 << 24);
+                    c0 = (ulong)lo0 | ((ulong)hi1 << 32);
+                }
+
+                {
+                    byte t0 = T0[lo1 & 0xFF];
+                    byte t1 = T1[(lo1 >> 8) & 0xFF];
+                    byte t2 = T2[(lo1 >> 16) & 0xFF];
+                    byte t3 = T3[lo1 >> 24];
+                    lo1 = (uint)t0 | ((uint)t1 << 8) | ((uint)t2 << 16) | ((uint)t3 << 24);
+                    byte t4 = T0[hi0 & 0xFF];
+                    byte t5 = T1[(hi0 >> 8) & 0xFF];
+                    byte t6 = T2[(hi0 >> 16) & 0xFF];
+                    byte t7 = T3[hi0 >> 24];
+                    hi0 = (uint)t4 | ((uint)t5 << 8) | ((uint)t6 << 16) | ((uint)t7 << 24);
+                    c1 = (ulong)lo1 | ((ulong)hi0 << 32);
+                }
+
+                if (--round == 0)
+                {
+                    break;
+                }
+
+                roundKey = roundKeys[round];
+                c0 ^= roundKey[0];
+                c1 ^= roundKey[1];
+            }
+
+            roundKey = roundKeys[0];
+            c0 -= roundKey[0];
+            c1 -= roundKey[1];
+
+            Pack.UInt64_To_LE(c0, output);
+            Pack.UInt64_To_LE(c1, output[8..]);
+        }
+
+        private void EncryptBlock_128(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            ulong c0 = Pack.LE_To_UInt64(input);
+            ulong c1 = Pack.LE_To_UInt64(input[8..]);
+
+            ulong[] roundKey = roundKeys[0];
+            c0 += roundKey[0];
+            c1 += roundKey[1];
+
+            for (int round = 0; ;)
+            {
+                uint lo0 = (uint)c0, hi0 = (uint)(c0 >> 32);
+                uint lo1 = (uint)c1, hi1 = (uint)(c1 >> 32);
+
+                {
+                    byte t0 = S0[lo0 & 0xFF];
+                    byte t1 = S1[(lo0 >> 8) & 0xFF];
+                    byte t2 = S2[(lo0 >> 16) & 0xFF];
+                    byte t3 = S3[lo0 >> 24];
+                    lo0 = (uint)t0 | ((uint)t1 << 8) | ((uint)t2 << 16) | ((uint)t3 << 24);
+                    byte t4 = S0[hi1 & 0xFF];
+                    byte t5 = S1[(hi1 >> 8) & 0xFF];
+                    byte t6 = S2[(hi1 >> 16) & 0xFF];
+                    byte t7 = S3[hi1 >> 24];
+                    hi1 = (uint)t4 | ((uint)t5 << 8) | ((uint)t6 << 16) | ((uint)t7 << 24);
+                    c0 = (ulong)lo0 | ((ulong)hi1 << 32);
+                }
+
+                {
+                    byte t0 = S0[lo1 & 0xFF];
+                    byte t1 = S1[(lo1 >> 8) & 0xFF];
+                    byte t2 = S2[(lo1 >> 16) & 0xFF];
+                    byte t3 = S3[lo1 >> 24];
+                    lo1 = (uint)t0 | ((uint)t1 << 8) | ((uint)t2 << 16) | ((uint)t3 << 24);
+                    byte t4 = S0[hi0 & 0xFF];
+                    byte t5 = S1[(hi0 >> 8) & 0xFF];
+                    byte t6 = S2[(hi0 >> 16) & 0xFF];
+                    byte t7 = S3[hi0 >> 24];
+                    hi0 = (uint)t4 | ((uint)t5 << 8) | ((uint)t6 << 16) | ((uint)t7 << 24);
+                    c1 = (ulong)lo1 | ((ulong)hi0 << 32);
+                }
+
+                c0 = MixColumn(c0);
+                c1 = MixColumn(c1);
+
+                if (++round == roundsAmount)
+                {
+                    break;
+                }
+
+                roundKey = roundKeys[round];
+                c0 ^= roundKey[0];
+                c1 ^= roundKey[1];
+            }
+
+            roundKey = roundKeys[roundsAmount];
+            c0 += roundKey[0];
+            c1 += roundKey[1];
+
+            Pack.UInt64_To_LE(c0, output);
+            Pack.UInt64_To_LE(c1, output[8..]);
+        }
+#else
         private void DecryptBlock_128(byte[] input, int inOff, byte[] output, int outOff)
         {
             ulong c0 = Pack.LE_To_UInt64(input, inOff);
@@ -466,6 +677,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             Pack.UInt64_To_LE(c0, output, outOff);
             Pack.UInt64_To_LE(c1, output, outOff + 8);
         }
+#endif
 
         private void SubBytes()
         {
@@ -900,7 +1112,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             }
         }
 
-        #region TABLES AND S-BOXES
+#region TABLES AND S-BOXES
 
         private const ulong mdsMatrix = 0x0407060801050101UL;
         private const ulong mdsInvMatrix = 0xCAD7492FA87695ADUL;
@@ -1057,7 +1269,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             0xf3, 0x83, 0x28, 0x32, 0x45, 0x1e, 0xa4, 0xd3, 0xa2, 0x46, 0x6e, 0x9c, 0xdd, 0x63, 0xd4, 0x9d
         };
 
-        #endregion
+#endregion
 
         public virtual string AlgorithmName
         {
diff --git a/crypto/src/crypto/engines/GOST28147Engine.cs b/crypto/src/crypto/engines/GOST28147Engine.cs
index 8ef8aeb13..ee5a1ba53 100644
--- a/crypto/src/crypto/engines/GOST28147Engine.cs
+++ b/crypto/src/crypto/engines/GOST28147Engine.cs
@@ -2,6 +2,7 @@ using System;
 using System.Collections.Generic;
 
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Engines
@@ -151,14 +152,10 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @param parameters the parameters required to set up the cipher.
 		* @exception ArgumentException if the parameters argument is inappropriate.
 		*/
-        public virtual void Init(
-			bool				forEncryption,
-			ICipherParameters	parameters)
+        public virtual void Init(bool forEncryption, ICipherParameters parameters)
 		{
-			if (parameters is ParametersWithSBox)
+			if (parameters is ParametersWithSBox param)
 			{
-				ParametersWithSBox   param = (ParametersWithSBox)parameters;
-
 				//
 				// Set the S-Box
 				//
@@ -173,14 +170,12 @@ namespace Org.BouncyCastle.Crypto.Engines
 				//
 				if (param.Parameters != null)
 				{
-					workingKey = generateWorkingKey(forEncryption,
-							((KeyParameter)param.Parameters).GetKey());
+					workingKey = GenerateWorkingKey(forEncryption, ((KeyParameter)param.Parameters).GetKey());
 				}
 			}
-			else if (parameters is KeyParameter)
+			else if (parameters is KeyParameter keyParameter)
 			{
-				workingKey = generateWorkingKey(forEncryption,
-									((KeyParameter)parameters).GetKey());
+				workingKey = GenerateWorkingKey(forEncryption, keyParameter.GetKey());
 			}
 			else if (parameters != null)
 			{
@@ -204,11 +199,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return BlockSize;
 		}
 
-        public virtual int ProcessBlock(
-			byte[]	input,
-			int		inOff,
-			byte[]	output,
-			int		outOff)
+        public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
 		{
 			if (workingKey == null)
 				throw new InvalidOperationException("Gost28147 engine not initialised");
@@ -216,30 +207,45 @@ namespace Org.BouncyCastle.Crypto.Engines
             Check.DataLength(input, inOff, BlockSize, "input buffer too short");
             Check.OutputLength(output, outOff, BlockSize, "output buffer too short");
 
-            Gost28147Func(workingKey, input, inOff, output, outOff);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			Gost28147Func(workingKey, input.AsSpan(inOff), output.AsSpan(outOff));
+#else
+			Gost28147Func(workingKey, input, inOff, output, outOff);
+#endif
 
 			return BlockSize;
 		}
 
-        public virtual void Reset()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
 		{
+			if (workingKey == null)
+				throw new InvalidOperationException("Gost28147 engine not initialised");
+
+			Check.DataLength(input, BlockSize, "input buffer too short");
+			Check.OutputLength(output, BlockSize, "output buffer too short");
+
+			Gost28147Func(workingKey, input, output);
+
+			return BlockSize;
 		}
+#endif
 
-		private int[] generateWorkingKey(
-			bool forEncryption,
-			byte[]  userKey)
+		public virtual void Reset()
+		{
+		}
+
+		private int[] GenerateWorkingKey(bool forEncryption, byte[] userKey)
 		{
 			this.forEncryption = forEncryption;
 
 			if (userKey.Length != 32)
-			{
 				throw new ArgumentException("Key length invalid. Key needs to be 32 byte - 256 bit!!!");
-			}
 
 			int[] key = new int[8];
-			for(int i=0; i!=8; i++)
+			for(int i=0; i != 8; i++)
 			{
-				key[i] = bytesToint(userKey,i*4);
+				key[i] = (int)Pack.LE_To_UInt32(userKey, i * 4);
 			}
 
 			return key;
@@ -267,16 +273,12 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return omLeft | omRight;
 		}
 
-		private void Gost28147Func(
-			int[]   workingKey,
-			byte[]  inBytes,
-			int     inOff,
-			byte[]  outBytes,
-			int     outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		private void Gost28147Func(int[] workingKey, ReadOnlySpan<byte> input, Span<byte> output)
 		{
-			int N1, N2, tmp;  //tmp -> for saving N1
-			N1 = bytesToint(inBytes, inOff);
-			N2 = bytesToint(inBytes, inOff + 4);
+			int N1 = (int)Pack.LE_To_UInt32(input);
+			int N2 = (int)Pack.LE_To_UInt32(input[4..]);
+			int tmp;  //tmp -> for saving N1
 
 			if (this.forEncryption)
 			{
@@ -322,30 +324,64 @@ namespace Org.BouncyCastle.Crypto.Engines
 
 			N2 = N2 ^ Gost28147_mainStep(N1, workingKey[0]);  // 32 step (N1=N1)
 
-			intTobytes(N1, outBytes, outOff);
-			intTobytes(N2, outBytes, outOff + 4);
+			Pack.UInt32_To_LE((uint)N1, output);
+			Pack.UInt32_To_LE((uint)N2, output[4..]);
 		}
-
-		//array of bytes to type int
-		private static int bytesToint(
-			byte[]  inBytes,
-			int     inOff)
+#else
+		private void Gost28147Func(int[] workingKey, byte[] inBytes, int inOff, byte[] outBytes, int outOff)
 		{
-			return  (int)((inBytes[inOff + 3] << 24) & 0xff000000) + ((inBytes[inOff + 2] << 16) & 0xff0000) +
-					((inBytes[inOff + 1] << 8) & 0xff00) + (inBytes[inOff] & 0xff);
-		}
+			int N1 = (int)Pack.LE_To_UInt32(inBytes, inOff);
+			int N2 = (int)Pack.LE_To_UInt32(inBytes, inOff + 4);
+			int tmp;  //tmp -> for saving N1
 
-		//int to array of bytes
-		private static void intTobytes(
-				int     num,
-				byte[]  outBytes,
-				int     outOff)
-		{
-				outBytes[outOff + 3] = (byte)(num >> 24);
-				outBytes[outOff + 2] = (byte)(num >> 16);
-				outBytes[outOff + 1] = (byte)(num >> 8);
-				outBytes[outOff] =     (byte)num;
+			if (this.forEncryption)
+			{
+			for(int k = 0; k < 3; k++)  // 1-24 steps
+			{
+				for(int j = 0; j < 8; j++)
+				{
+					tmp = N1;
+					int step = Gost28147_mainStep(N1, workingKey[j]);
+					N1 = N2 ^ step; // CM2
+					N2 = tmp;
+				}
+			}
+			for(int j = 7; j > 0; j--)  // 25-31 steps
+			{
+				tmp = N1;
+				N1 = N2 ^ Gost28147_mainStep(N1, workingKey[j]); // CM2
+				N2 = tmp;
+			}
+			}
+			else //decrypt
+			{
+			for(int j = 0; j < 8; j++)  // 1-8 steps
+			{
+				tmp = N1;
+				N1 = N2 ^ Gost28147_mainStep(N1, workingKey[j]); // CM2
+				N2 = tmp;
+			}
+			for(int k = 0; k < 3; k++)  //9-31 steps
+			{
+				for(int j = 7; j >= 0; j--)
+				{
+					if ((k == 2) && (j==0))
+					{
+						break; // break 32 step
+					}
+					tmp = N1;
+					N1 = N2 ^ Gost28147_mainStep(N1, workingKey[j]); // CM2
+					N2 = tmp;
+				}
+			}
+			}
+
+			N2 = N2 ^ Gost28147_mainStep(N1, workingKey[0]);  // 32 step (N1=N1)
+
+			Pack.UInt32_To_LE((uint)N1, outBytes, outOff);
+			Pack.UInt32_To_LE((uint)N2, outBytes, outOff + 4);
 		}
+#endif
 
 		/**
 		* Return the S-Box associated with SBoxName
diff --git a/crypto/src/crypto/engines/IdeaEngine.cs b/crypto/src/crypto/engines/IdeaEngine.cs
index 6c0379174..c5d3eb36f 100644
--- a/crypto/src/crypto/engines/IdeaEngine.cs
+++ b/crypto/src/crypto/engines/IdeaEngine.cs
@@ -64,11 +64,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return BLOCK_SIZE;
         }
 
-        public virtual int ProcessBlock(
-            byte[] input,
-            int inOff,
-            byte[] output,
-            int outOff)
+        public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
             if (workingKey == null)
                 throw new InvalidOperationException("IDEA engine not initialised");
@@ -76,28 +72,59 @@ namespace Org.BouncyCastle.Crypto.Engines
             Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
             Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            IdeaFunc(workingKey, input.AsSpan(inOff), output.AsSpan(outOff));
+#else
             IdeaFunc(workingKey, input, inOff, output, outOff);
+#endif
             return BLOCK_SIZE;
         }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            if (workingKey == null)
+                throw new InvalidOperationException("IDEA engine not initialised");
+
+            Check.DataLength(input, BLOCK_SIZE, "input buffer too short");
+            Check.OutputLength(output, BLOCK_SIZE, "output buffer too short");
+
+            IdeaFunc(workingKey, input, output);
+            return BLOCK_SIZE;
+        }
+#endif
+
         public virtual void Reset()
         {
         }
-        private static readonly int    MASK = 0xffff;
-        private static readonly int    BASE = 0x10001;
-        private int BytesToWord(
-            byte[]  input,
-            int     inOff)
+
+        private static readonly int MASK = 0xffff;
+        private static readonly int BASE = 0x10001;
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private int BytesToWord(ReadOnlySpan<byte> input)
+        {
+            return ((input[0] << 8) & 0xff00) + (input[1] & 0xff);
+        }
+
+        private void WordToBytes(int word, Span<byte> output)
+        {
+            output[0] = (byte)((uint)word >> 8);
+            output[1] = (byte)word;
+        }
+#else
+        private int BytesToWord(byte[] input, int inOff)
         {
             return ((input[inOff] << 8) & 0xff00) + (input[inOff + 1] & 0xff);
         }
-        private void WordToBytes(
-            int     word,
-            byte[]  outBytes,
-            int     outOff)
+
+        private void WordToBytes(int word, byte[] outBytes, int outOff)
         {
             outBytes[outOff] = (byte)((uint) word >> 8);
             outBytes[outOff + 1] = (byte)word;
         }
+#endif
+
         /**
         * return x = x * y where the multiplication is done modulo
         * 65537 (0x10001) (as defined in the IDEA specification) and
@@ -128,19 +155,51 @@ namespace Org.BouncyCastle.Crypto.Engines
             }
             return x & MASK;
         }
-        private void IdeaFunc(
-            int[]   workingKey,
-            byte[]  input,
-            int     inOff,
-            byte[]  outBytes,
-            int     outOff)
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void IdeaFunc(int[] workingKey, ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            int x0 = BytesToWord(input);
+            int x1 = BytesToWord(input[2..]);
+            int x2 = BytesToWord(input[4..]);
+            int x3 = BytesToWord(input[6..]);
+            int keyOff = 0, t0, t1;
+            for (int round = 0; round < 8; round++)
+            {
+                x0 = Mul(x0, workingKey[keyOff++]);
+                x1 += workingKey[keyOff++];
+                x1 &= MASK;
+                x2 += workingKey[keyOff++];
+                x2 &= MASK;
+                x3 = Mul(x3, workingKey[keyOff++]);
+                t0 = x1;
+                t1 = x2;
+                x2 ^= x0;
+                x1 ^= x3;
+                x2 = Mul(x2, workingKey[keyOff++]);
+                x1 += x2;
+                x1 &= MASK;
+                x1 = Mul(x1, workingKey[keyOff++]);
+                x2 += x1;
+                x2 &= MASK;
+                x0 ^= x1;
+                x3 ^= x2;
+                x1 ^= t1;
+                x2 ^= t0;
+            }
+            WordToBytes(Mul(x0, workingKey[keyOff++]), output);
+            WordToBytes(x2 + workingKey[keyOff++], output[2..]);  /* NB: Order */
+            WordToBytes(x1 + workingKey[keyOff++], output[4..]);
+            WordToBytes(Mul(x3, workingKey[keyOff]), output[6..]);
+        }
+#else
+        private void IdeaFunc(int[] workingKey, byte[] input, int inOff, byte[] outBytes, int outOff)
         {
-            int     x0, x1, x2, x3, t0, t1;
-            int     keyOff = 0;
-            x0 = BytesToWord(input, inOff);
-            x1 = BytesToWord(input, inOff + 2);
-            x2 = BytesToWord(input, inOff + 4);
-            x3 = BytesToWord(input, inOff + 6);
+            int x0 = BytesToWord(input, inOff);
+            int x1 = BytesToWord(input, inOff + 2);
+            int x2 = BytesToWord(input, inOff + 4);
+            int x3 = BytesToWord(input, inOff + 6);
+            int keyOff = 0, t0, t1;
             for (int round = 0; round < 8; round++)
             {
                 x0 = Mul(x0, workingKey[keyOff++]);
@@ -169,16 +228,17 @@ namespace Org.BouncyCastle.Crypto.Engines
             WordToBytes(x1 + workingKey[keyOff++], outBytes, outOff + 4);
             WordToBytes(Mul(x3, workingKey[keyOff]), outBytes, outOff + 6);
         }
+#endif
+
         /**
         * The following function is used to expand the user key to the encryption
         * subkey. The first 16 bytes are the user key, and the rest of the subkey
         * is calculated by rotating the previous 16 bytes by 25 bits to the left,
         * and so on until the subkey is completed.
         */
-        private int[] ExpandKey(
-            byte[]  uKey)
+        private int[] ExpandKey(byte[] uKey)
         {
-            int[]   key = new int[52];
+            int[] key = new int[52];
             if (uKey.Length < 16)
             {
                 byte[]  tmp = new byte[16];
@@ -187,7 +247,11 @@ namespace Org.BouncyCastle.Crypto.Engines
             }
             for (int i = 0; i < 8; i++)
             {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                key[i] = BytesToWord(uKey[(i * 2)..]);
+#else
                 key[i] = BytesToWord(uKey, i * 2);
+#endif
             }
             for (int i = 8; i < 52; i++)
             {
diff --git a/crypto/src/crypto/engines/NoekeonEngine.cs b/crypto/src/crypto/engines/NoekeonEngine.cs
index 838a40339..2866d8d75 100644
--- a/crypto/src/crypto/engines/NoekeonEngine.cs
+++ b/crypto/src/crypto/engines/NoekeonEngine.cs
@@ -92,11 +92,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             this._initialised = true;
         }
 
-        public virtual int ProcessBlock(
-			byte[]	input,
-			int		inOff,
-			byte[]	output,
-			int		outOff)
+        public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
 		{
 			if (!_initialised)
 				throw new InvalidOperationException(AlgorithmName + " not initialised");
@@ -104,15 +100,179 @@ namespace Org.BouncyCastle.Crypto.Engines
             Check.DataLength(input, inOff, Size, "input buffer too short");
             Check.OutputLength(output, outOff, Size, "output buffer too short");
 
-            return _forEncryption
-				?	EncryptBlock(input, inOff, output, outOff)
-				:	DecryptBlock(input, inOff, output, outOff);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			return _forEncryption
+				? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff))
+				: DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+#else
+			return _forEncryption
+				? EncryptBlock(input, inOff, output, outOff)
+				: DecryptBlock(input, inOff, output, outOff);
+#endif
 		}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			if (!_initialised)
+				throw new InvalidOperationException(AlgorithmName + " not initialised");
+
+			Check.DataLength(input, Size, "input buffer too short");
+			Check.OutputLength(output, Size, "output buffer too short");
+
+			return _forEncryption
+				? EncryptBlock(input, output)
+				: DecryptBlock(input, output);
+		}
+#endif
+
 		public virtual void Reset()
 		{
 		}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			uint a0 = Pack.BE_To_UInt32(input);
+			uint a1 = Pack.BE_To_UInt32(input[4..]);
+			uint a2 = Pack.BE_To_UInt32(input[8..]);
+			uint a3 = Pack.BE_To_UInt32(input[12..]);
+
+			uint k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
+
+			int round = 0;
+			for (;;)
+			{
+				a0 ^= RoundConstants[round];
+
+				// theta(a, k);
+				{
+					uint t02 = a0 ^ a2;
+					t02 ^= Integers.RotateLeft(t02, 8) ^ Integers.RotateLeft(t02, 24);
+
+					a0 ^= k0;
+					a1 ^= k1;
+					a2 ^= k2;
+					a3 ^= k3;
+
+                    uint t13 = a1 ^ a3;
+                    t13 ^= Integers.RotateLeft(t13, 8) ^ Integers.RotateLeft(t13, 24);
+
+                    a0 ^= t13;
+                    a1 ^= t02;
+                    a2 ^= t13;
+                    a3 ^= t02;
+				}
+
+				if (++round > Size)
+					break;
+
+				// pi1(a);
+				{
+					a1 = Integers.RotateLeft(a1, 1);
+					a2 = Integers.RotateLeft(a2, 5);
+					a3 = Integers.RotateLeft(a3, 2);
+				}
+
+				// gamma(a);
+				{
+                    uint t = a3;
+                    a1 ^= a3 | a2;
+                    a3 = a0 ^ (a2 & ~a1);
+
+                    a2 = t ^ ~a1 ^ a2 ^ a3;
+
+                    a1 ^= a3 | a2;
+                    a0 = t ^ (a2 & a1);
+				}
+
+				// pi2(a);
+				{
+					a1 = Integers.RotateLeft(a1, 31);
+					a2 = Integers.RotateLeft(a2, 27);
+					a3 = Integers.RotateLeft(a3, 30);
+				}
+			}
+
+			Pack.UInt32_To_BE(a0, output);
+			Pack.UInt32_To_BE(a1, output[4..]);
+			Pack.UInt32_To_BE(a2, output[8..]);
+			Pack.UInt32_To_BE(a3, output[12..]);
+
+			return Size;
+		}
+
+		private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			uint a0 = Pack.BE_To_UInt32(input);
+			uint a1 = Pack.BE_To_UInt32(input[4..]);
+			uint a2 = Pack.BE_To_UInt32(input[8..]);
+			uint a3 = Pack.BE_To_UInt32(input[12..]);
+
+			uint k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
+
+			int round = Size;
+			for (;;)
+			{
+				// theta(a, k);
+				{
+                    uint t02 = a0 ^ a2;
+                    t02 ^= Integers.RotateLeft(t02, 8) ^ Integers.RotateLeft(t02, 24);
+
+                    a0 ^= k0;
+                    a1 ^= k1;
+                    a2 ^= k2;
+                    a3 ^= k3;
+
+                    uint t13 = a1 ^ a3;
+                    t13 ^= Integers.RotateLeft(t13, 8) ^ Integers.RotateLeft(t13, 24);
+
+                    a0 ^= t13;
+                    a1 ^= t02;
+                    a2 ^= t13;
+                    a3 ^= t02;
+                }
+
+                a0 ^= RoundConstants[round];
+
+				if (--round < 0)
+					break;
+
+				// pi1(a);
+				{
+					a1 = Integers.RotateLeft(a1, 1);
+					a2 = Integers.RotateLeft(a2, 5);
+					a3 = Integers.RotateLeft(a3, 2);
+				}
+
+				// gamma(a);
+				{
+                    uint t = a3;
+                    a1 ^= a3 | a2;
+                    a3 = a0 ^ (a2 & ~a1);
+
+                    a2 = t ^ ~a1 ^ a2 ^ a3;
+
+                    a1 ^= a3 | a2;
+                    a0 = t ^ (a2 & a1);
+                }
+
+                // pi2(a);
+                {
+					a1 = Integers.RotateLeft(a1, 31);
+					a2 = Integers.RotateLeft(a2, 27);
+					a3 = Integers.RotateLeft(a3, 30);
+				}
+			}
+
+			Pack.UInt32_To_BE(a0, output);
+			Pack.UInt32_To_BE(a1, output[4..]);
+			Pack.UInt32_To_BE(a2, output[8..]);
+			Pack.UInt32_To_BE(a3, output[12..]);
+
+			return Size;
+		}
+#else
 		private int EncryptBlock(byte[]	input, int inOff, byte[] output, int outOff)
 		{
 			uint a0 = Pack.BE_To_UInt32(input, inOff);
@@ -254,5 +414,6 @@ namespace Org.BouncyCastle.Crypto.Engines
 
 			return Size;
 		}
+#endif
 	}
 }
diff --git a/crypto/src/crypto/engines/NullEngine.cs b/crypto/src/crypto/engines/NullEngine.cs
index f883b7c29..b79cfba93 100644
--- a/crypto/src/crypto/engines/NullEngine.cs
+++ b/crypto/src/crypto/engines/NullEngine.cs
@@ -41,11 +41,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return BlockSize;
 		}
 
-        public virtual int ProcessBlock(
-			byte[]	input,
-			int		inOff,
-			byte[]	output,
-			int		outOff)
+        public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
 		{
 			if (!initialised)
 				throw new InvalidOperationException("Null engine not initialised");
@@ -61,7 +57,22 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return BlockSize;
 		}
 
-        public virtual void Reset()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			if (!initialised)
+				throw new InvalidOperationException("Null engine not initialised");
+
+            Check.DataLength(input, BlockSize, "input buffer too short");
+            Check.OutputLength(output, BlockSize, "output buffer too short");
+
+			input[..BlockSize].CopyTo(output);
+
+			return BlockSize;
+		}
+#endif
+
+		public virtual void Reset()
 		{
 			// nothing needs to be done
 		}
diff --git a/crypto/src/crypto/engines/RC2Engine.cs b/crypto/src/crypto/engines/RC2Engine.cs
index 4aca1894f..972c4128a 100644
--- a/crypto/src/crypto/engines/RC2Engine.cs
+++ b/crypto/src/crypto/engines/RC2Engine.cs
@@ -159,11 +159,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return BLOCK_SIZE;
         }
 
-        public virtual int ProcessBlock(
-            byte[]	input,
-            int		inOff,
-            byte[]	output,
-            int		outOff)
+        public virtual int ProcessBlock(byte[] input, int inOff, byte[]	output, int outOff)
         {
             if (workingKey == null)
                 throw new InvalidOperationException("RC2 engine not initialised");
@@ -171,6 +167,16 @@ namespace Org.BouncyCastle.Crypto.Engines
             Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
             Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            if (encrypting)
+            {
+                EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+            }
+            else
+            {
+                DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+            }
+#else
             if (encrypting)
             {
                 EncryptBlock(input, inOff, output, outOff);
@@ -179,26 +185,150 @@ namespace Org.BouncyCastle.Crypto.Engines
             {
                 DecryptBlock(input, inOff, output, outOff);
             }
+#endif
+
+            return BLOCK_SIZE;
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            if (workingKey == null)
+                throw new InvalidOperationException("RC2 engine not initialised");
+
+            Check.DataLength(input, BLOCK_SIZE, "input buffer too short");
+            Check.OutputLength(output, BLOCK_SIZE, "output buffer too short");
+
+            if (encrypting)
+            {
+                EncryptBlock(input, output);
+            }
+            else
+            {
+                DecryptBlock(input, output);
+            }
 
             return BLOCK_SIZE;
         }
+#endif
 
         /**
         * return the result rotating the 16 bit number in x left by y
         */
-        private int RotateWordLeft(
-            int x,
-            int y)
+        private int RotateWordLeft(int x, int y)
         {
             x &= 0xffff;
             return (x << y) | (x >> (16 - y));
         }
 
-        private void EncryptBlock(
-            byte[]  input,
-            int     inOff,
-            byte[]  outBytes,
-            int     outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            int x76, x54, x32, x10;
+
+            x76 = ((input[7] & 0xff) << 8) + (input[6] & 0xff);
+            x54 = ((input[5] & 0xff) << 8) + (input[4] & 0xff);
+            x32 = ((input[3] & 0xff) << 8) + (input[2] & 0xff);
+            x10 = ((input[1] & 0xff) << 8) + (input[0] & 0xff);
+
+            for (int i = 0; i <= 16; i += 4)
+            {
+                x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i], 1);
+                x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i + 1], 2);
+                x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i + 2], 3);
+                x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i + 3], 5);
+            }
+
+            x10 += workingKey[x76 & 63];
+            x32 += workingKey[x10 & 63];
+            x54 += workingKey[x32 & 63];
+            x76 += workingKey[x54 & 63];
+
+            for (int i = 20; i <= 40; i += 4)
+            {
+                x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i], 1);
+                x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i + 1], 2);
+                x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i + 2], 3);
+                x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i + 3], 5);
+            }
+
+            x10 += workingKey[x76 & 63];
+            x32 += workingKey[x10 & 63];
+            x54 += workingKey[x32 & 63];
+            x76 += workingKey[x54 & 63];
+
+            for (int i = 44; i < 64; i += 4)
+            {
+                x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i], 1);
+                x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i + 1], 2);
+                x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i + 2], 3);
+                x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i + 3], 5);
+            }
+
+            output[0] = (byte)x10;
+            output[1] = (byte)(x10 >> 8);
+            output[2] = (byte)x32;
+            output[3] = (byte)(x32 >> 8);
+            output[4] = (byte)x54;
+            output[5] = (byte)(x54 >> 8);
+            output[6] = (byte)x76;
+            output[7] = (byte)(x76 >> 8);
+        }
+
+        private void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            int x76, x54, x32, x10;
+
+            x76 = ((input[7] & 0xff) << 8) + (input[6] & 0xff);
+            x54 = ((input[5] & 0xff) << 8) + (input[4] & 0xff);
+            x32 = ((input[3] & 0xff) << 8) + (input[2] & 0xff);
+            x10 = ((input[1] & 0xff) << 8) + (input[0] & 0xff);
+
+            for (int i = 60; i >= 44; i -= 4)
+            {
+                x76 = RotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i + 3]);
+                x54 = RotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i + 2]);
+                x32 = RotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i + 1]);
+                x10 = RotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i]);
+            }
+
+            x76 -= workingKey[x54 & 63];
+            x54 -= workingKey[x32 & 63];
+            x32 -= workingKey[x10 & 63];
+            x10 -= workingKey[x76 & 63];
+
+            for (int i = 40; i >= 20; i -= 4)
+            {
+                x76 = RotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i + 3]);
+                x54 = RotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i + 2]);
+                x32 = RotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i + 1]);
+                x10 = RotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i]);
+            }
+
+            x76 -= workingKey[x54 & 63];
+            x54 -= workingKey[x32 & 63];
+            x32 -= workingKey[x10 & 63];
+            x10 -= workingKey[x76 & 63];
+
+            for (int i = 16; i >= 0; i -= 4)
+            {
+                x76 = RotateWordLeft(x76, 11) - ((x10 & ~x54) + (x32 & x54) + workingKey[i + 3]);
+                x54 = RotateWordLeft(x54, 13) - ((x76 & ~x32) + (x10 & x32) + workingKey[i + 2]);
+                x32 = RotateWordLeft(x32, 14) - ((x54 & ~x10) + (x76 & x10) + workingKey[i + 1]);
+                x10 = RotateWordLeft(x10, 15) - ((x32 & ~x76) + (x54 & x76) + workingKey[i]);
+            }
+
+            output[0] = (byte)x10;
+            output[1] = (byte)(x10 >> 8);
+            output[2] = (byte)x32;
+            output[3] = (byte)(x32 >> 8);
+            output[4] = (byte)x54;
+            output[5] = (byte)(x54 >> 8);
+            output[6] = (byte)x76;
+            output[7] = (byte)(x76 >> 8);
+        }
+#else
+        private void EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
         {
             int x76, x54, x32, x10;
 
@@ -209,10 +339,10 @@ namespace Org.BouncyCastle.Crypto.Engines
 
             for (int i = 0; i <= 16; i += 4)
             {
-                    x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i  ], 1);
-                    x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2);
-                    x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3);
-                    x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5);
+                x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i  ], 1);
+                x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2);
+                x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3);
+                x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5);
             }
 
             x10 += workingKey[x76 & 63];
@@ -222,10 +352,10 @@ namespace Org.BouncyCastle.Crypto.Engines
 
             for (int i = 20; i <= 40; i += 4)
             {
-                    x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i  ], 1);
-                    x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2);
-                    x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3);
-                    x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5);
+                x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i  ], 1);
+                x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2);
+                x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3);
+                x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5);
             }
 
             x10 += workingKey[x76 & 63];
@@ -235,10 +365,10 @@ namespace Org.BouncyCastle.Crypto.Engines
 
             for (int i = 44; i < 64; i += 4)
             {
-                    x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i  ], 1);
-                    x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2);
-                    x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3);
-                    x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5);
+                x10 = RotateWordLeft(x10 + (x32 & ~x76) + (x54 & x76) + workingKey[i  ], 1);
+                x32 = RotateWordLeft(x32 + (x54 & ~x10) + (x76 & x10) + workingKey[i+1], 2);
+                x54 = RotateWordLeft(x54 + (x76 & ~x32) + (x10 & x32) + workingKey[i+2], 3);
+                x76 = RotateWordLeft(x76 + (x10 & ~x54) + (x32 & x54) + workingKey[i+3], 5);
             }
 
             outBytes[outOff + 0] = (byte)x10;
@@ -251,11 +381,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             outBytes[outOff + 7] = (byte)(x76 >> 8);
         }
 
-        private void DecryptBlock(
-            byte[]  input,
-            int     inOff,
-            byte[]  outBytes,
-            int     outOff)
+        private void DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
         {
             int x76, x54, x32, x10;
 
@@ -307,5 +433,6 @@ namespace Org.BouncyCastle.Crypto.Engines
             outBytes[outOff + 6] = (byte)x76;
             outBytes[outOff + 7] = (byte)(x76 >> 8);
         }
+#endif
     }
 }
diff --git a/crypto/src/crypto/engines/RC532Engine.cs b/crypto/src/crypto/engines/RC532Engine.cs
index d1c29e624..aa3da5870 100644
--- a/crypto/src/crypto/engines/RC532Engine.cs
+++ b/crypto/src/crypto/engines/RC532Engine.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Engines
@@ -46,7 +47,6 @@ namespace Org.BouncyCastle.Crypto.Engines
         public RC532Engine()
         {
             _noRounds     = 12;         // the default
-//            _S            = null;
         }
 
         public virtual string AlgorithmName
@@ -72,23 +72,17 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @exception ArgumentException if the parameters argument is
         * inappropriate.
         */
-        public virtual void Init(
-            bool				forEncryption,
-            ICipherParameters	parameters)
+        public virtual void Init(bool forEncryption, ICipherParameters parameters)
         {
-            if (typeof(RC5Parameters).IsInstanceOfType(parameters))
+            if (parameters is RC5Parameters rc5Parameters)
             {
-                RC5Parameters p = (RC5Parameters)parameters;
+                _noRounds = rc5Parameters.Rounds;
 
-                _noRounds = p.Rounds;
-
-                SetKey(p.GetKey());
+                SetKey(rc5Parameters.GetKey());
             }
-            else if (typeof(KeyParameter).IsInstanceOfType(parameters))
+            else if (parameters is KeyParameter keyParameter)
             {
-                KeyParameter p = (KeyParameter)parameters;
-
-                SetKey(p.GetKey());
+                SetKey(keyParameter.GetKey());
             }
             else
             {
@@ -98,16 +92,27 @@ namespace Org.BouncyCastle.Crypto.Engines
             this.forEncryption = forEncryption;
         }
 
-        public virtual int ProcessBlock(
-            byte[]	input,
-            int		inOff,
-            byte[]	output,
-            int		outOff)
+        public virtual int ProcessBlock(byte[] input, int inOff, byte[]	output, int outOff)
+        {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return forEncryption
+                ? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff))
+                : DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+#else
+            return forEncryption
+				? EncryptBlock(input, inOff, output, outOff)
+				: DecryptBlock(input, inOff, output, outOff);
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
-            return (forEncryption)
-				?	EncryptBlock(input, inOff, output, outOff)
-				:	DecryptBlock(input, inOff, output, outOff);
+            return forEncryption
+                ? EncryptBlock(input, output)
+                : DecryptBlock(input, output);
         }
+#endif
 
         public virtual void Reset()
         {
@@ -118,8 +123,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         *
         * @param  key  the key to be used
         */
-        private void SetKey(
-            byte[] key)
+        private void SetKey(byte[] key)
         {
             //
             // KEY EXPANSION:
@@ -133,7 +137,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             //   of K. Any unfilled byte positions in L are zeroed. In the
             //   case that b = c = 0, set c = 1 and L[0] = 0.
             //
-            int[]   L = new int[(key.Length + (4 - 1)) / 4];
+            int[]   L = new int[(key.Length + 3) / 4];
 
             for (int i = 0; i != key.Length; i++)
             {
@@ -175,120 +179,81 @@ namespace Org.BouncyCastle.Crypto.Engines
 
             for (int k = 0; k < iter; k++)
             {
-                A = _S[ii] = RotateLeft(_S[ii] + A + B, 3);
-                B =  L[jj] = RotateLeft( L[jj] + A + B, A+B);
+                A = _S[ii] = Integers.RotateLeft(_S[ii] + A + B, 3);
+                B =  L[jj] = Integers.RotateLeft(L[jj] + A + B, A + B);
                 ii = (ii+1) % _S.Length;
                 jj = (jj+1) %  L.Length;
             }
         }
 
-        /**
-        * Encrypt the given block starting at the given offset and place
-        * the result in the provided buffer starting at the given offset.
-        *
-        * @param  in     in byte buffer containing data to encrypt
-        * @param  inOff  offset into src buffer
-        * @param  out     out buffer where encrypted data is written
-        * @param  outOff  offset into out buffer
-        */
-        private int EncryptBlock(
-            byte[]  input,
-            int     inOff,
-            byte[]  outBytes,
-            int     outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
-            int A = BytesToWord(input, inOff) + _S[0];
-            int B = BytesToWord(input, inOff + 4) + _S[1];
+            int A = (int)Pack.LE_To_UInt32(input) + _S[0];
+            int B = (int)Pack.LE_To_UInt32(input[4..]) + _S[1];
 
             for (int i = 1; i <= _noRounds; i++)
             {
-                A = RotateLeft(A ^ B, B) + _S[2*i];
-                B = RotateLeft(B ^ A, A) + _S[2*i+1];
+                A = Integers.RotateLeft(A ^ B, B) + _S[2*i];
+                B = Integers.RotateLeft(B ^ A, A) + _S[2*i+1];
             }
 
-            WordToBytes(A, outBytes, outOff);
-            WordToBytes(B, outBytes, outOff + 4);
+            Pack.UInt32_To_LE((uint)A, output);
+            Pack.UInt32_To_LE((uint)B, output[4..]);
 
-            return 2 * 4;
+            return 8;
         }
 
-        private int DecryptBlock(
-            byte[]  input,
-            int     inOff,
-            byte[]  outBytes,
-            int     outOff)
+        private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
-            int A = BytesToWord(input, inOff);
-            int B = BytesToWord(input, inOff + 4);
+            int A = (int)Pack.LE_To_UInt32(input);
+            int B = (int)Pack.LE_To_UInt32(input[4..]);
 
             for (int i = _noRounds; i >= 1; i--)
             {
-                B = RotateRight(B - _S[2*i+1], A) ^ A;
-                A = RotateRight(A - _S[2*i],   B) ^ B;
+                B = Integers.RotateRight(B - _S[2*i+1], A) ^ A;
+                A = Integers.RotateRight(A - _S[2*i],   B) ^ B;
             }
 
-            WordToBytes(A - _S[0], outBytes, outOff);
-            WordToBytes(B - _S[1], outBytes, outOff + 4);
+            Pack.UInt32_To_LE((uint)(A - _S[0]), output);
+            Pack.UInt32_To_LE((uint)(B - _S[1]), output[4..]);
 
-            return 2 * 4;
+            return 8;
         }
+#else
+        private int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
+        {
+            int A = (int)Pack.LE_To_UInt32(input, inOff) + _S[0];
+            int B = (int)Pack.LE_To_UInt32(input, inOff + 4) + _S[1];
 
+            for (int i = 1; i <= _noRounds; i++)
+            {
+                A = Integers.RotateLeft(A ^ B, B) + _S[2*i];
+                B = Integers.RotateLeft(B ^ A, A) + _S[2*i+1];
+            }
 
-        //////////////////////////////////////////////////////////////
-        //
-        // PRIVATE Helper Methods
-        //
-        //////////////////////////////////////////////////////////////
+            Pack.UInt32_To_LE((uint)A, outBytes, outOff);
+            Pack.UInt32_To_LE((uint)B, outBytes, outOff + 4);
 
-        /**
-        * Perform a left "spin" of the word. The rotation of the given
-        * word <em>x</em> is rotated left by <em>y</em> bits.
-        * Only the <em>lg(32)</em> low-order bits of <em>y</em>
-        * are used to determine the rotation amount. Here it is
-        * assumed that the wordsize used is a power of 2.
-        *
-        * @param  x  word to rotate
-        * @param  y    number of bits to rotate % 32
-        */
-        private int RotateLeft(int x, int y) {
-            return ((int)  (  (uint) (x << (y & (32-1))) |
-                              ((uint) x >> (32 - (y & (32-1)))) )
-                   );
-        }
-
-        /**
-        * Perform a right "spin" of the word. The rotation of the given
-        * word <em>x</em> is rotated left by <em>y</em> bits.
-        * Only the <em>lg(32)</em> low-order bits of <em>y</em>
-        * are used to determine the rotation amount. Here it is
-        * assumed that the wordsize used is a power of 2.
-        *
-        * @param  x  word to rotate
-        * @param  y    number of bits to rotate % 32
-        */
-        private int RotateRight(int x, int y) {
-            return ((int) (     ((uint) x >> (y & (32-1))) |
-                                (uint) (x << (32 - (y & (32-1))))   )
-                   );
+            return 8;
         }
 
-        private int BytesToWord(
-            byte[]  src,
-            int     srcOff)
+        private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
         {
-            return (src[srcOff] & 0xff) | ((src[srcOff + 1] & 0xff) << 8)
-                | ((src[srcOff + 2] & 0xff) << 16) | ((src[srcOff + 3] & 0xff) << 24);
-        }
+            int A = (int)Pack.LE_To_UInt32(input, inOff);
+            int B = (int)Pack.LE_To_UInt32(input, inOff + 4);
 
-        private void WordToBytes(
-            int    word,
-            byte[]  dst,
-            int     dstOff)
-        {
-            dst[dstOff] = (byte)word;
-            dst[dstOff + 1] = (byte)(word >> 8);
-            dst[dstOff + 2] = (byte)(word >> 16);
-            dst[dstOff + 3] = (byte)(word >> 24);
+            for (int i = _noRounds; i >= 1; i--)
+            {
+                B = Integers.RotateRight(B - _S[2*i+1], A) ^ A;
+                A = Integers.RotateRight(A - _S[2*i],   B) ^ B;
+            }
+
+            Pack.UInt32_To_LE((uint)(A - _S[0]), outBytes, outOff);
+            Pack.UInt32_To_LE((uint)(B - _S[1]), outBytes, outOff + 4);
+
+            return 8;
         }
+#endif
     }
 }
diff --git a/crypto/src/crypto/engines/RC564Engine.cs b/crypto/src/crypto/engines/RC564Engine.cs
index 097fd60ba..8d524f420 100644
--- a/crypto/src/crypto/engines/RC564Engine.cs
+++ b/crypto/src/crypto/engines/RC564Engine.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Engines
@@ -15,9 +16,6 @@ namespace Org.BouncyCastle.Crypto.Engines
     public class RC564Engine
 		: IBlockCipher
     {
-        private static readonly int wordSize = 64;
-        private static readonly int bytesPerWord = wordSize / 8;
-
         /*
         * the number of rounds to perform
         */
@@ -64,7 +62,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 
         public virtual int GetBlockSize()
         {
-            return 2 * bytesPerWord;
+            return 16;
         }
 
         /**
@@ -75,33 +73,39 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @exception ArgumentException if the parameters argument is
         * inappropriate.
         */
-        public virtual void Init(
-            bool             forEncryption,
-            ICipherParameters    parameters)
+        public virtual void Init(bool forEncryption, ICipherParameters parameters)
         {
-            if (!(typeof(RC5Parameters).IsInstanceOfType(parameters)))
-            {
+            if (!(parameters is RC5Parameters rc5Parameters))
                 throw new ArgumentException("invalid parameter passed to RC564 init - " + Platform.GetTypeName(parameters));
-            }
-
-            RC5Parameters       p = (RC5Parameters)parameters;
 
             this.forEncryption = forEncryption;
 
-            _noRounds     = p.Rounds;
+            _noRounds = rc5Parameters.Rounds;
 
-            SetKey(p.GetKey());
+            SetKey(rc5Parameters.GetKey());
+        }
+
+        public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
+        {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return forEncryption
+                ? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff))
+                : DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+#else
+            return forEncryption
+                ? EncryptBlock(input, inOff, output, outOff)
+                : DecryptBlock(input, inOff, output, outOff);
+#endif
         }
 
-        public virtual int ProcessBlock(
-            byte[]  input,
-            int     inOff,
-            byte[]  output,
-            int     outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
-            return (forEncryption) ? EncryptBlock(input, inOff, output, outOff)
-                                        : DecryptBlock(input, inOff, output, outOff);
+            return forEncryption
+                ? EncryptBlock(input, output)
+                : DecryptBlock(input, output);
         }
+#endif
 
         public virtual void Reset()
         {
@@ -112,8 +116,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         *
         * @param  key  the key to be used
         */
-        private void SetKey(
-            byte[]      key)
+        private void SetKey(byte[] key)
         {
             //
             // KEY EXPANSION:
@@ -127,11 +130,11 @@ namespace Org.BouncyCastle.Crypto.Engines
             //   of K. Any unfilled byte positions in L are zeroed. In the
             //   case that b = c = 0, set c = 1 and L[0] = 0.
             //
-            long[]   L = new long[(key.Length + (bytesPerWord - 1)) / bytesPerWord];
+            long[] L = new long[(key.Length + 7) / 8];
 
             for (int i = 0; i != key.Length; i++)
             {
-                L[i / bytesPerWord] += (long)(key[i] & 0xff) << (8 * (i % bytesPerWord));
+                L[i / 8] += (long)(key[i] & 0xff) << (8 * (i % 8));
             }
 
             //
@@ -169,127 +172,81 @@ namespace Org.BouncyCastle.Crypto.Engines
 
             for (int k = 0; k < iter; k++)
             {
-                A = _S[ii] = RotateLeft(_S[ii] + A + B, 3);
-                B =  L[jj] = RotateLeft( L[jj] + A + B, A+B);
+                A = _S[ii] = Longs.RotateLeft(_S[ii] + A + B, 3);
+                B =  L[jj] = Longs.RotateLeft(L[jj] + A + B, (int)(A + B));
                 ii = (ii+1) % _S.Length;
                 jj = (jj+1) %  L.Length;
             }
         }
 
-        /**
-        * Encrypt the given block starting at the given offset and place
-        * the result in the provided buffer starting at the given offset.
-        *
-        * @param  in      in byte buffer containing data to encrypt
-        * @param  inOff   offset into src buffer
-        * @param  out     out buffer where encrypted data is written
-        * @param  outOff  offset into out buffer
-        */
-        private int EncryptBlock(
-            byte[]  input,
-            int     inOff,
-            byte[]  outBytes,
-            int     outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
-            long A = BytesToWord(input, inOff) + _S[0];
-            long B = BytesToWord(input, inOff + bytesPerWord) + _S[1];
+            long A = (long)Pack.LE_To_UInt64(input) + _S[0];
+            long B = (long)Pack.LE_To_UInt64(input[8..]) + _S[1];
 
             for (int i = 1; i <= _noRounds; i++)
             {
-                A = RotateLeft(A ^ B, B) + _S[2*i];
-                B = RotateLeft(B ^ A, A) + _S[2*i+1];
+                A = Longs.RotateLeft(A ^ B, (int)B) + _S[2*i];
+                B = Longs.RotateLeft(B ^ A, (int)A) + _S[2*i+1];
             }
 
-            WordToBytes(A, outBytes, outOff);
-            WordToBytes(B, outBytes, outOff + bytesPerWord);
+            Pack.UInt64_To_LE((ulong)A, output);
+            Pack.UInt64_To_LE((ulong)B, output[8..]);
 
-            return 2 * bytesPerWord;
+            return 16;
         }
 
-        private int DecryptBlock(
-            byte[]  input,
-            int     inOff,
-            byte[]  outBytes,
-            int     outOff)
+        private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
-            long A = BytesToWord(input, inOff);
-            long B = BytesToWord(input, inOff + bytesPerWord);
+            long A = (long)Pack.LE_To_UInt64(input);
+            long B = (long)Pack.LE_To_UInt64(input[8..]);
 
             for (int i = _noRounds; i >= 1; i--)
             {
-                B = RotateRight(B - _S[2*i+1], A) ^ A;
-                A = RotateRight(A - _S[2*i],   B) ^ B;
+                B = Longs.RotateRight(B - _S[2*i+1], (int)A) ^ A;
+                A = Longs.RotateRight(A - _S[2*i], (int)B) ^ B;
             }
 
-            WordToBytes(A - _S[0], outBytes, outOff);
-            WordToBytes(B - _S[1], outBytes, outOff + bytesPerWord);
+            Pack.UInt64_To_LE((ulong)(A - _S[0]), output);
+            Pack.UInt64_To_LE((ulong)(B - _S[1]), output[8..]);
 
-            return 2 * bytesPerWord;
+            return 16;
         }
+#else
+        private int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
+        {
+            long A = (long)Pack.LE_To_UInt64(input, inOff) + _S[0];
+            long B = (long)Pack.LE_To_UInt64(input, inOff + 8) + _S[1];
 
+            for (int i = 1; i <= _noRounds; i++)
+            {
+                A = Longs.RotateLeft(A ^ B, (int)B) + _S[2*i];
+                B = Longs.RotateLeft(B ^ A, (int)A) + _S[2*i+1];
+            }
 
-        //////////////////////////////////////////////////////////////
-        //
-        // PRIVATE Helper Methods
-        //
-        //////////////////////////////////////////////////////////////
-
-        /**
-        * Perform a left "spin" of the word. The rotation of the given
-        * word <em>x</em> is rotated left by <em>y</em> bits.
-        * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
-        * are used to determine the rotation amount. Here it is
-        * assumed that the wordsize used is a power of 2.
-        *
-        * @param  x  word to rotate
-        * @param  y    number of bits to rotate % wordSize
-        */
-        private long RotateLeft(long x, long y) {
-            return ((long) (    (ulong) (x << (int) (y & (wordSize-1))) |
-                                ((ulong) x >> (int) (wordSize - (y & (wordSize-1)))))
-                   );
-        }
+            Pack.UInt64_To_LE((ulong)A, outBytes, outOff);
+            Pack.UInt64_To_LE((ulong)B, outBytes, outOff + 8);
 
-        /**
-        * Perform a right "spin" of the word. The rotation of the given
-        * word <em>x</em> is rotated left by <em>y</em> bits.
-        * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
-        * are used to determine the rotation amount. Here it is
-        * assumed that the wordsize used is a power of 2.
-        *
-        * @param x word to rotate
-        * @param y number of bits to rotate % wordSize
-        */
-        private long RotateRight(long x, long y) {
-            return ((long) (    ((ulong) x >> (int) (y & (wordSize-1))) |
-                                (ulong) (x << (int) (wordSize - (y & (wordSize-1)))))
-                   );
+            return 16;
         }
 
-        private long BytesToWord(
-            byte[]  src,
-            int     srcOff)
+        private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
         {
-            long    word = 0;
+            long A = (long)Pack.LE_To_UInt64(input, inOff);
+            long B = (long)Pack.LE_To_UInt64(input, inOff + 8);
 
-            for (int i = bytesPerWord - 1; i >= 0; i--)
+            for (int i = _noRounds; i >= 1; i--)
             {
-                word = (word << 8) + (src[i + srcOff] & 0xff);
+                B = Longs.RotateRight(B - _S[2*i+1], (int)A) ^ A;
+                A = Longs.RotateRight(A - _S[2*i], (int)B) ^ B;
             }
 
-            return word;
-        }
+            Pack.UInt64_To_LE((ulong)(A - _S[0]), outBytes, outOff);
+            Pack.UInt64_To_LE((ulong)(B - _S[1]), outBytes, outOff + 8);
 
-        private void WordToBytes(
-            long    word,
-            byte[]  dst,
-            int     dstOff)
-        {
-            for (int i = 0; i < bytesPerWord; i++)
-            {
-                dst[i + dstOff] = (byte)word;
-                word = (long) ((ulong) word >> 8);
-            }
+            return 16;
         }
+#endif
     }
 }
diff --git a/crypto/src/crypto/engines/RC6Engine.cs b/crypto/src/crypto/engines/RC6Engine.cs
index 9aeb1e7cb..316bae65e 100644
--- a/crypto/src/crypto/engines/RC6Engine.cs
+++ b/crypto/src/crypto/engines/RC6Engine.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Engines
@@ -11,9 +12,6 @@ namespace Org.BouncyCastle.Crypto.Engines
     public class RC6Engine
 		: IBlockCipher
     {
-        private static readonly int wordSize = 32;
-        private static readonly int bytesPerWord = wordSize / 8;
-
         /*
         * the number of rounds to perform
         */
@@ -46,7 +44,6 @@ namespace Org.BouncyCastle.Crypto.Engines
         */
         public RC6Engine()
         {
-//            _S            = null;
         }
 
         public virtual string AlgorithmName
@@ -61,7 +58,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 
         public virtual int GetBlockSize()
         {
-            return 4 * bytesPerWord;
+            return 16;
         }
 
         /**
@@ -72,36 +69,51 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @exception ArgumentException if the parameters argument is
         * inappropriate.
         */
-        public virtual void Init(
-            bool				forEncryption,
-            ICipherParameters	parameters)
+        public virtual void Init(bool forEncryption, ICipherParameters parameters)
         {
-            if (!(parameters is KeyParameter))
+            if (!(parameters is KeyParameter keyParameter))
                 throw new ArgumentException("invalid parameter passed to RC6 init - " + Platform.GetTypeName(parameters));
 
             this.forEncryption = forEncryption;
 
-			KeyParameter p = (KeyParameter)parameters;
-			SetKey(p.GetKey());
+			SetKey(keyParameter.GetKey());
         }
 
-        public virtual int ProcessBlock(
-            byte[]	input,
-            int		inOff,
-            byte[]	output,
-            int		outOff)
+        public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
-			int blockSize = GetBlockSize();
 			if (_S == null)
 				throw new InvalidOperationException("RC6 engine not initialised");
 
+            int blockSize = GetBlockSize();
             Check.DataLength(input, inOff, blockSize, "input buffer too short");
             Check.OutputLength(output, outOff, blockSize, "output buffer too short");
 
-            return (forEncryption)
-				?	EncryptBlock(input, inOff, output, outOff)
-				:	DecryptBlock(input, inOff, output, outOff);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return forEncryption
+                ? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff))
+                : DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+#else
+            return forEncryption
+				? EncryptBlock(input, inOff, output, outOff)
+				: DecryptBlock(input, inOff, output, outOff);
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            if (_S == null)
+                throw new InvalidOperationException("RC6 engine not initialised");
+
+            int blockSize = GetBlockSize();
+            Check.DataLength(input, blockSize, "input buffer too short");
+            Check.OutputLength(output, blockSize, "output buffer too short");
+
+            return forEncryption
+                ? EncryptBlock(input, output)
+                : DecryptBlock(input, output);
         }
+#endif
 
         public virtual void Reset()
         {
@@ -128,17 +140,17 @@ namespace Org.BouncyCastle.Crypto.Engines
             //   case that b = c = 0, set c = 1 and L[0] = 0.
             //
             // compute number of dwords
-            int c = (key.Length + (bytesPerWord - 1)) / bytesPerWord;
+            int c = (key.Length + 3) / 4;
             if (c == 0)
             {
                 c = 1;
             }
-            int[]   L = new int[(key.Length + bytesPerWord - 1) / bytesPerWord];
+            int[]   L = new int[(key.Length + 3) / 4];
 
             // load all key bytes into array of key dwords
             for (int i = key.Length - 1; i >= 0; i--)
             {
-                L[i / bytesPerWord] = (L[i / bytesPerWord] << 8) + (key[i] & 0xff);
+                L[i / 4] = (L[i / 4] << 8) + (key[i] & 0xff);
             }
 
             //
@@ -178,24 +190,21 @@ namespace Org.BouncyCastle.Crypto.Engines
 
             for (int k = 0; k < iter; k++)
             {
-                A = _S[ii] = RotateLeft(_S[ii] + A + B, 3);
-                B =  L[jj] = RotateLeft( L[jj] + A + B, A+B);
+                A = _S[ii] = Integers.RotateLeft(_S[ii] + A + B, 3);
+                B =  L[jj] = Integers.RotateLeft( L[jj] + A + B, A + B);
                 ii = (ii+1) % _S.Length;
                 jj = (jj+1) %  L.Length;
             }
         }
 
-        private int EncryptBlock(
-            byte[]  input,
-            int     inOff,
-            byte[]  outBytes,
-            int     outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
             // load A,B,C and D registers from in.
-            int A = BytesToWord(input, inOff);
-            int B = BytesToWord(input, inOff + bytesPerWord);
-            int C = BytesToWord(input, inOff + bytesPerWord*2);
-            int D = BytesToWord(input, inOff + bytesPerWord*3);
+            int A = (int)Pack.LE_To_UInt32(input);
+            int B = (int)Pack.LE_To_UInt32(input[4..]);
+            int C = (int)Pack.LE_To_UInt32(input[8..]);
+            int D = (int)Pack.LE_To_UInt32(input[12..]);
 
             // Do pseudo-round #0: pre-whitening of B and D
             B += _S[0];
@@ -204,21 +213,21 @@ namespace Org.BouncyCastle.Crypto.Engines
             // perform round #1,#2 ... #ROUNDS of encryption
             for (int i = 1; i <= _noRounds; i++)
             {
-                int t = 0,u = 0;
+                int t = 0, u = 0;
 
-                t = B*(2*B+1);
-                t = RotateLeft(t,5);
+                t = B * (2 * B + 1);
+                t = Integers.RotateLeft(t, 5);
 
-                u = D*(2*D+1);
-                u = RotateLeft(u,5);
+                u = D * (2 * D + 1);
+                u = Integers.RotateLeft(u, 5);
 
                 A ^= t;
-                A = RotateLeft(A,u);
-                A += _S[2*i];
+                A = Integers.RotateLeft(A, u);
+                A += _S[2 * i];
 
                 C ^= u;
-                C = RotateLeft(C,t);
-                C += _S[2*i+1];
+                C = Integers.RotateLeft(C, t);
+                C += _S[2 * i + 1];
 
                 int temp = A;
                 A = B;
@@ -226,39 +235,36 @@ namespace Org.BouncyCastle.Crypto.Engines
                 C = D;
                 D = temp;
             }
+
             // do pseudo-round #(ROUNDS+1) : post-whitening of A and C
-            A += _S[2*_noRounds+2];
-            C += _S[2*_noRounds+3];
+            A += _S[2 * _noRounds + 2];
+            C += _S[2 * _noRounds + 3];
 
             // store A, B, C and D registers to out
-            WordToBytes(A, outBytes, outOff);
-            WordToBytes(B, outBytes, outOff + bytesPerWord);
-            WordToBytes(C, outBytes, outOff + bytesPerWord*2);
-            WordToBytes(D, outBytes, outOff + bytesPerWord*3);
+            Pack.UInt32_To_LE((uint)A, output);
+            Pack.UInt32_To_LE((uint)B, output[4..]);
+            Pack.UInt32_To_LE((uint)C, output[8..]);
+            Pack.UInt32_To_LE((uint)D, output[12..]);
 
-            return 4 * bytesPerWord;
+            return 16;
         }
 
-        private int DecryptBlock(
-            byte[]  input,
-            int     inOff,
-            byte[]  outBytes,
-            int     outOff)
+        private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
             // load A,B,C and D registers from out.
-            int A = BytesToWord(input, inOff);
-            int B = BytesToWord(input, inOff + bytesPerWord);
-            int C = BytesToWord(input, inOff + bytesPerWord*2);
-            int D = BytesToWord(input, inOff + bytesPerWord*3);
+            int A = (int)Pack.LE_To_UInt32(input);
+            int B = (int)Pack.LE_To_UInt32(input[4..]);
+            int C = (int)Pack.LE_To_UInt32(input[8..]);
+            int D = (int)Pack.LE_To_UInt32(input[12..]);
 
             // Undo pseudo-round #(ROUNDS+1) : post whitening of A and C
-            C -= _S[2*_noRounds+3];
-            A -= _S[2*_noRounds+2];
+            C -= _S[2 * _noRounds + 3];
+            A -= _S[2 * _noRounds + 2];
 
             // Undo round #ROUNDS, .., #2,#1 of encryption
             for (int i = _noRounds; i >= 1; i--)
             {
-                int t=0,u = 0;
+                int t = 0, u = 0;
 
                 int temp = D;
                 D = C;
@@ -266,96 +272,133 @@ namespace Org.BouncyCastle.Crypto.Engines
                 B = A;
                 A = temp;
 
-                t = B*(2*B+1);
-                t = RotateLeft(t, LGW);
+                t = B * (2 * B + 1);
+                t = Integers.RotateLeft(t, LGW);
 
-                u = D*(2*D+1);
-                u = RotateLeft(u, LGW);
+                u = D * (2 * D + 1);
+                u = Integers.RotateLeft(u, LGW);
 
-                C -= _S[2*i+1];
-                C = RotateRight(C,t);
+                C -= _S[2 * i + 1];
+                C = Integers.RotateRight(C, t);
                 C ^= u;
 
-                A -= _S[2*i];
-                A = RotateRight(A,u);
+                A -= _S[2 * i];
+                A = Integers.RotateRight(A, u);
                 A ^= t;
-
             }
+
             // Undo pseudo-round #0: pre-whitening of B and D
             D -= _S[1];
             B -= _S[0];
 
-            WordToBytes(A, outBytes, outOff);
-            WordToBytes(B, outBytes, outOff + bytesPerWord);
-            WordToBytes(C, outBytes, outOff + bytesPerWord*2);
-            WordToBytes(D, outBytes, outOff + bytesPerWord*3);
+            Pack.UInt32_To_LE((uint)A, output);
+            Pack.UInt32_To_LE((uint)B, output[4..]);
+            Pack.UInt32_To_LE((uint)C, output[8..]);
+            Pack.UInt32_To_LE((uint)D, output[12..]);
 
-            return 4 * bytesPerWord;
+            return 16;
         }
+#else
+        private int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
+        {
+            // load A,B,C and D registers from in.
+            int A = (int)Pack.LE_To_UInt32(input, inOff);
+            int B = (int)Pack.LE_To_UInt32(input, inOff + 4);
+            int C = (int)Pack.LE_To_UInt32(input, inOff + 8);
+            int D = (int)Pack.LE_To_UInt32(input, inOff + 12);
 
+            // Do pseudo-round #0: pre-whitening of B and D
+            B += _S[0];
+            D += _S[1];
 
-        //////////////////////////////////////////////////////////////
-        //
-        // PRIVATE Helper Methods
-        //
-        //////////////////////////////////////////////////////////////
+            // perform round #1,#2 ... #ROUNDS of encryption
+            for (int i = 1; i <= _noRounds; i++)
+            {
+                int t = 0,u = 0;
 
-        /**
-        * Perform a left "spin" of the word. The rotation of the given
-        * word <em>x</em> is rotated left by <em>y</em> bits.
-        * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
-        * are used to determine the rotation amount. Here it is
-        * assumed that the wordsize used is a power of 2.
-        *
-        * @param x word to rotate
-        * @param y number of bits to rotate % wordSize
-        */
-        private int RotateLeft(int x, int y)
-        {
-            return ((int)((uint)(x << (y & (wordSize-1)))
-				| ((uint) x >> (wordSize - (y & (wordSize-1))))));
-        }
+                t = B*(2*B+1);
+                t = Integers.RotateLeft(t,5);
 
-        /**
-        * Perform a right "spin" of the word. The rotation of the given
-        * word <em>x</em> is rotated left by <em>y</em> bits.
-        * Only the <em>lg(wordSize)</em> low-order bits of <em>y</em>
-        * are used to determine the rotation amount. Here it is
-        * assumed that the wordsize used is a power of 2.
-        *
-        * @param x word to rotate
-        * @param y number of bits to rotate % wordSize
-        */
-        private int RotateRight(int x, int y) 
-		{
-            return ((int)(((uint) x >> (y & (wordSize-1)))
-				| (uint)(x << (wordSize - (y & (wordSize-1))))));
-        }
+                u = D*(2*D+1);
+                u = Integers.RotateLeft(u,5);
 
-        private int BytesToWord(
-            byte[]	src,
-            int		srcOff)
-        {
-            int word = 0;
+                A ^= t;
+                A = Integers.RotateLeft(A,u);
+                A += _S[2*i];
 
-            for (int i = bytesPerWord - 1; i >= 0; i--)
-            {
-                word = (word << 8) + (src[i + srcOff] & 0xff);
+                C ^= u;
+                C = Integers.RotateLeft(C,t);
+                C += _S[2*i+1];
+
+                int temp = A;
+                A = B;
+                B = C;
+                C = D;
+                D = temp;
             }
 
-            return word;
+            // do pseudo-round #(ROUNDS+1) : post-whitening of A and C
+            A += _S[2*_noRounds+2];
+            C += _S[2*_noRounds+3];
+
+            // store A, B, C and D registers to out
+            Pack.UInt32_To_LE((uint)A, outBytes, outOff);
+            Pack.UInt32_To_LE((uint)B, outBytes, outOff + 4);
+            Pack.UInt32_To_LE((uint)C, outBytes, outOff + 8);
+            Pack.UInt32_To_LE((uint)D, outBytes, outOff + 12);
+
+            return 16;
         }
 
-        private void WordToBytes(
-            int		word,
-            byte[]	dst,
-            int		dstOff)
+        private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
         {
-            for (int i = 0; i < bytesPerWord; i++)
+            // load A,B,C and D registers from out.
+            int A = (int)Pack.LE_To_UInt32(input, inOff);
+            int B = (int)Pack.LE_To_UInt32(input, inOff + 4);
+            int C = (int)Pack.LE_To_UInt32(input, inOff + 8);
+            int D = (int)Pack.LE_To_UInt32(input, inOff + 12);
+
+            // Undo pseudo-round #(ROUNDS+1) : post whitening of A and C
+            C -= _S[2*_noRounds+3];
+            A -= _S[2*_noRounds+2];
+
+            // Undo round #ROUNDS, .., #2,#1 of encryption
+            for (int i = _noRounds; i >= 1; i--)
             {
-                dst[i + dstOff] = (byte)word;
-                word = (int) ((uint) word >> 8);
+                int t=0,u = 0;
+
+                int temp = D;
+                D = C;
+                C = B;
+                B = A;
+                A = temp;
+
+                t = B*(2*B+1);
+                t = Integers.RotateLeft(t, LGW);
+
+                u = D*(2*D+1);
+                u = Integers.RotateLeft(u, LGW);
+
+                C -= _S[2*i+1];
+                C = Integers.RotateRight(C,t);
+                C ^= u;
+
+                A -= _S[2*i];
+                A = Integers.RotateRight(A,u);
+                A ^= t;
             }
+
+            // Undo pseudo-round #0: pre-whitening of B and D
+            D -= _S[1];
+            B -= _S[0];
+
+            Pack.UInt32_To_LE((uint)A, outBytes, outOff);
+            Pack.UInt32_To_LE((uint)B, outBytes, outOff + 4);
+            Pack.UInt32_To_LE((uint)C, outBytes, outOff + 8);
+            Pack.UInt32_To_LE((uint)D, outBytes, outOff + 12);
+
+            return 16;
         }
+#endif
     }
 }
diff --git a/crypto/src/crypto/engines/RijndaelEngine.cs b/crypto/src/crypto/engines/RijndaelEngine.cs
index 7025cb5dc..2bd404973 100644
--- a/crypto/src/crypto/engines/RijndaelEngine.cs
+++ b/crypto/src/crypto/engines/RijndaelEngine.cs
@@ -601,11 +601,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return BC / 2;
 		}
 
-        public virtual int ProcessBlock(
-			byte[]	input,
-			int		inOff,
-			byte[]	output,
-			int		outOff)
+        public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
 		{
 			if (workingKey == null)
 				throw new InvalidOperationException("Rijndael engine not initialised");
@@ -613,7 +609,11 @@ namespace Org.BouncyCastle.Crypto.Engines
             Check.DataLength(input, inOff, (BC / 2), "input buffer too short");
             Check.OutputLength(output, outOff, (BC / 2), "output buffer too short");
 
-            UnPackBlock(input, inOff);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			UnPackBlock(input.AsSpan(inOff));
+#else
+			UnPackBlock(input, inOff);
+#endif
 
 			if (forEncryption)
 			{
@@ -624,20 +624,80 @@ namespace Org.BouncyCastle.Crypto.Engines
 				DecryptBlock(workingKey);
 			}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			PackBlock(output.AsSpan(outOff));
+#else
 			PackBlock(output, outOff);
+#endif
 
 			return BC / 2;
 		}
 
-        public virtual void Reset()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
 		{
+			if (workingKey == null)
+				throw new InvalidOperationException("Rijndael engine not initialised");
+
+			Check.DataLength(input, (BC / 2), "input buffer too short");
+			Check.OutputLength(output, (BC / 2), "output buffer too short");
+
+			UnPackBlock(input);
+
+			if (forEncryption)
+			{
+				EncryptBlock(workingKey);
+			}
+			else
+			{
+				DecryptBlock(workingKey);
+			}
+
+			PackBlock(output);
+
+			return BC / 2;
 		}
+#endif
 
-		private void UnPackBlock(
-			byte[]      bytes,
-			int         off)
+		public virtual void Reset()
+		{
+		}
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		private void UnPackBlock(ReadOnlySpan<byte> input)
+		{
+			int index = 0;
+
+			A0 = (long)(input[index++] & 0xff);
+			A1 = (long)(input[index++] & 0xff);
+			A2 = (long)(input[index++] & 0xff);
+			A3 = (long)(input[index++] & 0xff);
+
+			for (int j = 8; j != BC; j += 8)
+			{
+				A0 |= (long)(input[index++] & 0xff) << j;
+				A1 |= (long)(input[index++] & 0xff) << j;
+				A2 |= (long)(input[index++] & 0xff) << j;
+				A3 |= (long)(input[index++] & 0xff) << j;
+			}
+		}
+
+		private void PackBlock(Span<byte> output)
+		{
+			int index = 0;
+
+			for (int j = 0; j != BC; j += 8)
+			{
+				output[index++] = (byte)(A0 >> j);
+				output[index++] = (byte)(A1 >> j);
+				output[index++] = (byte)(A2 >> j);
+				output[index++] = (byte)(A3 >> j);
+			}
+		}
+#else
+		private void UnPackBlock(byte[] bytes, int off)
 		{
-			int     index = off;
+			int index = off;
 
 			A0 = (long)(bytes[index++] & 0xff);
 			A1 = (long)(bytes[index++] & 0xff);
@@ -653,11 +713,9 @@ namespace Org.BouncyCastle.Crypto.Engines
 			}
 		}
 
-		private  void PackBlock(
-			byte[]      bytes,
-			int         off)
+		private void PackBlock(byte[] bytes, int off)
 		{
-			int     index = off;
+			int index = off;
 
 			for (int j = 0; j != BC; j += 8)
 			{
@@ -667,8 +725,9 @@ namespace Org.BouncyCastle.Crypto.Engines
 				bytes[index++] = (byte)(A3 >> j);
 			}
 		}
+#endif
 
-		private  void EncryptBlock(
+		private void EncryptBlock(
 			long[][] rk)
 		{
 			int r;
diff --git a/crypto/src/crypto/engines/SEEDEngine.cs b/crypto/src/crypto/engines/SEEDEngine.cs
index d4142c867..6b511e4cc 100644
--- a/crypto/src/crypto/engines/SEEDEngine.cs
+++ b/crypto/src/crypto/engines/SEEDEngine.cs
@@ -1,5 +1,7 @@
 using System;
+
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Engines
 {
@@ -168,12 +170,10 @@ namespace Org.BouncyCastle.Crypto.Engines
 		private int[] wKey;
 		private bool forEncryption;
 
-        public virtual void Init(
-			bool				forEncryption,
-			ICipherParameters	parameters)
+        public virtual void Init(bool forEncryption, ICipherParameters parameters)
 		{
 			this.forEncryption = forEncryption;
-			wKey = createWorkingKey(((KeyParameter)parameters).GetKey());
+			wKey = CreateWorkingKey(((KeyParameter)parameters).GetKey());
 		}
 
         public virtual string AlgorithmName
@@ -191,11 +191,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return BlockSize;
 		}
 
-        public virtual int ProcessBlock(
-			byte[]	inBuf,
-			int		inOff,
-			byte[]	outBuf,
-			int		outOff)
+        public virtual int ProcessBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff)
 		{
 			if (wKey == null)
 				throw new InvalidOperationException("SEED engine not initialised");
@@ -203,8 +199,47 @@ namespace Org.BouncyCastle.Crypto.Engines
             Check.DataLength(inBuf, inOff, BlockSize, "input buffer too short");
             Check.OutputLength(outBuf, outOff, BlockSize, "output buffer too short");
 
-            long l = bytesToLong(inBuf, inOff + 0);
-			long r = bytesToLong(inBuf, inOff + 8);
+            long l = (long)Pack.BE_To_UInt64(inBuf, inOff + 0);
+			long r = (long)Pack.BE_To_UInt64(inBuf, inOff + 8);
+
+			if (forEncryption)
+			{
+				for (int i = 0; i < 16; i++)
+				{
+					long nl = r;
+
+					r = l ^ F(wKey[2 * i], wKey[(2 * i) + 1], r);
+					l = nl;
+				}
+			}
+			else
+			{
+				for (int i = 15; i >= 0; i--)
+				{
+					long nl = r;
+
+					r = l ^ F(wKey[2 * i], wKey[(2 * i) + 1], r);
+					l = nl;
+				}
+			}
+
+			Pack.UInt64_To_BE((ulong)r, outBuf, outOff + 0);
+			Pack.UInt64_To_BE((ulong)l, outBuf, outOff + 8);
+
+			return BlockSize;
+		}
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			if (wKey == null)
+				throw new InvalidOperationException("SEED engine not initialised");
+
+			Check.DataLength(input, BlockSize, "input buffer too short");
+			Check.OutputLength(output, BlockSize, "output buffer too short");
+
+			long l = (long)Pack.BE_To_UInt64(input);
+			long r = (long)Pack.BE_To_UInt64(input[8..]);
 
 			if (forEncryption)
 			{
@@ -227,25 +262,25 @@ namespace Org.BouncyCastle.Crypto.Engines
 				}
 			}
 
-			longToBytes(outBuf, outOff + 0, r);
-			longToBytes(outBuf, outOff + 8, l);
+			Pack.UInt64_To_BE((ulong)r, output);
+			Pack.UInt64_To_BE((ulong)l, output[8..]);
 
 			return BlockSize;
 		}
+#endif
 
-        public virtual void Reset()
+		public virtual void Reset()
 		{
 		}
 
-		private int[] createWorkingKey(
-			byte[] inKey)
+		private int[] CreateWorkingKey(byte[] inKey)
 		{
 			if (inKey.Length != 16)
 				throw new ArgumentException("key size must be 128 bits");
 
 			int[] key = new int[32];
-			long lower = bytesToLong(inKey, 0);
-			long upper = bytesToLong(inKey, 8);
+			long lower = (long)Pack.BE_To_UInt64(inKey, 0);
+			long upper = (long)Pack.BE_To_UInt64(inKey, 8);
 
 			int key0 = extractW0(lower);
 			int key1 = extractW1(lower);
@@ -298,31 +333,6 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return ((long)((ulong) x >> 8)) | (x << 56);
 		}
 
-		private long bytesToLong(
-			byte[]	src,
-			int		srcOff)
-		{
-			long word = 0;
-
-			for (int i = 0; i <= 7; i++)
-			{
-				word = (word << 8) + (src[i + srcOff] & 0xff);
-			}
-
-			return word;
-		}
-
-		private void longToBytes(
-			byte[]	dest,
-			int		destOff,
-			long	value)
-		{
-			for (int i = 0; i < 8; i++)
-			{
-				dest[i + destOff] = (byte)(value >> ((7 - i) * 8));
-			}
-		}
-
 		private int G(
 			int x)
 		{
diff --git a/crypto/src/crypto/engines/SM4Engine.cs b/crypto/src/crypto/engines/SM4Engine.cs
index 7477b070e..6a7206a01 100644
--- a/crypto/src/crypto/engines/SM4Engine.cs
+++ b/crypto/src/crypto/engines/SM4Engine.cs
@@ -182,6 +182,37 @@ namespace Org.BouncyCastle.Crypto.Engines
             return BlockSize;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            if (null == rk)
+                throw new InvalidOperationException("SM4 not initialised");
+
+            Check.DataLength(input, BlockSize, "input buffer too short");
+            Check.OutputLength(output, BlockSize, "output buffer too short");
+
+            uint X0 = Pack.BE_To_UInt32(input);
+            uint X1 = Pack.BE_To_UInt32(input[4..]);
+            uint X2 = Pack.BE_To_UInt32(input[8..]);
+            uint X3 = Pack.BE_To_UInt32(input[12..]);
+
+            for (int i = 0; i < 32; i += 4)
+            {
+                X0 ^= T(X1 ^ X2 ^ X3 ^ rk[i    ]);  // F0
+                X1 ^= T(X2 ^ X3 ^ X0 ^ rk[i + 1]);  // F1
+                X2 ^= T(X3 ^ X0 ^ X1 ^ rk[i + 2]);  // F2
+                X3 ^= T(X0 ^ X1 ^ X2 ^ rk[i + 3]);  // F3
+            }
+
+            Pack.UInt32_To_BE(X3, output);
+            Pack.UInt32_To_BE(X2, output[4..]);
+            Pack.UInt32_To_BE(X1, output[8..]);
+            Pack.UInt32_To_BE(X0, output[12..]);
+
+            return BlockSize;
+        }
+#endif
+
         public virtual void Reset()
         {
         }
diff --git a/crypto/src/crypto/engines/SerpentEngine.cs b/crypto/src/crypto/engines/SerpentEngine.cs
index 76799f045..00473fa0a 100644
--- a/crypto/src/crypto/engines/SerpentEngine.cs
+++ b/crypto/src/crypto/engines/SerpentEngine.cs
@@ -150,14 +150,130 @@ namespace Org.BouncyCastle.Crypto.Engines
             return w;
         }
 
-        /**
-        * Encrypt one block of plaintext.
-        *
-        * @param input the array containing the input data.
-        * @param inOff offset into the in array the data starts at.
-        * @param output the array the output data will be copied into.
-        * @param outOff the offset into the out array the output will start at.
-        */
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        protected override void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            X0 = (int)Pack.LE_To_UInt32(input);
+            X1 = (int)Pack.LE_To_UInt32(input[4..]);
+            X2 = (int)Pack.LE_To_UInt32(input[8..]);
+            X3 = (int)Pack.LE_To_UInt32(input[12..]);
+
+            Sb0(wKey[0] ^ X0, wKey[1] ^ X1, wKey[2] ^ X2, wKey[3] ^ X3); LT();
+            Sb1(wKey[4] ^ X0, wKey[5] ^ X1, wKey[6] ^ X2, wKey[7] ^ X3); LT();
+            Sb2(wKey[8] ^ X0, wKey[9] ^ X1, wKey[10] ^ X2, wKey[11] ^ X3); LT();
+            Sb3(wKey[12] ^ X0, wKey[13] ^ X1, wKey[14] ^ X2, wKey[15] ^ X3); LT();
+            Sb4(wKey[16] ^ X0, wKey[17] ^ X1, wKey[18] ^ X2, wKey[19] ^ X3); LT();
+            Sb5(wKey[20] ^ X0, wKey[21] ^ X1, wKey[22] ^ X2, wKey[23] ^ X3); LT();
+            Sb6(wKey[24] ^ X0, wKey[25] ^ X1, wKey[26] ^ X2, wKey[27] ^ X3); LT();
+            Sb7(wKey[28] ^ X0, wKey[29] ^ X1, wKey[30] ^ X2, wKey[31] ^ X3); LT();
+            Sb0(wKey[32] ^ X0, wKey[33] ^ X1, wKey[34] ^ X2, wKey[35] ^ X3); LT();
+            Sb1(wKey[36] ^ X0, wKey[37] ^ X1, wKey[38] ^ X2, wKey[39] ^ X3); LT();
+            Sb2(wKey[40] ^ X0, wKey[41] ^ X1, wKey[42] ^ X2, wKey[43] ^ X3); LT();
+            Sb3(wKey[44] ^ X0, wKey[45] ^ X1, wKey[46] ^ X2, wKey[47] ^ X3); LT();
+            Sb4(wKey[48] ^ X0, wKey[49] ^ X1, wKey[50] ^ X2, wKey[51] ^ X3); LT();
+            Sb5(wKey[52] ^ X0, wKey[53] ^ X1, wKey[54] ^ X2, wKey[55] ^ X3); LT();
+            Sb6(wKey[56] ^ X0, wKey[57] ^ X1, wKey[58] ^ X2, wKey[59] ^ X3); LT();
+            Sb7(wKey[60] ^ X0, wKey[61] ^ X1, wKey[62] ^ X2, wKey[63] ^ X3); LT();
+            Sb0(wKey[64] ^ X0, wKey[65] ^ X1, wKey[66] ^ X2, wKey[67] ^ X3); LT();
+            Sb1(wKey[68] ^ X0, wKey[69] ^ X1, wKey[70] ^ X2, wKey[71] ^ X3); LT();
+            Sb2(wKey[72] ^ X0, wKey[73] ^ X1, wKey[74] ^ X2, wKey[75] ^ X3); LT();
+            Sb3(wKey[76] ^ X0, wKey[77] ^ X1, wKey[78] ^ X2, wKey[79] ^ X3); LT();
+            Sb4(wKey[80] ^ X0, wKey[81] ^ X1, wKey[82] ^ X2, wKey[83] ^ X3); LT();
+            Sb5(wKey[84] ^ X0, wKey[85] ^ X1, wKey[86] ^ X2, wKey[87] ^ X3); LT();
+            Sb6(wKey[88] ^ X0, wKey[89] ^ X1, wKey[90] ^ X2, wKey[91] ^ X3); LT();
+            Sb7(wKey[92] ^ X0, wKey[93] ^ X1, wKey[94] ^ X2, wKey[95] ^ X3); LT();
+            Sb0(wKey[96] ^ X0, wKey[97] ^ X1, wKey[98] ^ X2, wKey[99] ^ X3); LT();
+            Sb1(wKey[100] ^ X0, wKey[101] ^ X1, wKey[102] ^ X2, wKey[103] ^ X3); LT();
+            Sb2(wKey[104] ^ X0, wKey[105] ^ X1, wKey[106] ^ X2, wKey[107] ^ X3); LT();
+            Sb3(wKey[108] ^ X0, wKey[109] ^ X1, wKey[110] ^ X2, wKey[111] ^ X3); LT();
+            Sb4(wKey[112] ^ X0, wKey[113] ^ X1, wKey[114] ^ X2, wKey[115] ^ X3); LT();
+            Sb5(wKey[116] ^ X0, wKey[117] ^ X1, wKey[118] ^ X2, wKey[119] ^ X3); LT();
+            Sb6(wKey[120] ^ X0, wKey[121] ^ X1, wKey[122] ^ X2, wKey[123] ^ X3); LT();
+            Sb7(wKey[124] ^ X0, wKey[125] ^ X1, wKey[126] ^ X2, wKey[127] ^ X3);
+
+            Pack.UInt32_To_LE((uint)(wKey[128] ^ X0), output);
+            Pack.UInt32_To_LE((uint)(wKey[129] ^ X1), output[4..]);
+            Pack.UInt32_To_LE((uint)(wKey[130] ^ X2), output[8..]);
+            Pack.UInt32_To_LE((uint)(wKey[131] ^ X3), output[12..]);
+        }
+
+        protected override void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            X0 = wKey[128] ^ (int)Pack.LE_To_UInt32(input);
+            X1 = wKey[129] ^ (int)Pack.LE_To_UInt32(input[4..]);
+            X2 = wKey[130] ^ (int)Pack.LE_To_UInt32(input[8..]);
+            X3 = wKey[131] ^ (int)Pack.LE_To_UInt32(input[12..]);
+
+            Ib7(X0, X1, X2, X3);
+            X0 ^= wKey[124]; X1 ^= wKey[125]; X2 ^= wKey[126]; X3 ^= wKey[127];
+            InverseLT(); Ib6(X0, X1, X2, X3);
+            X0 ^= wKey[120]; X1 ^= wKey[121]; X2 ^= wKey[122]; X3 ^= wKey[123];
+            InverseLT(); Ib5(X0, X1, X2, X3);
+            X0 ^= wKey[116]; X1 ^= wKey[117]; X2 ^= wKey[118]; X3 ^= wKey[119];
+            InverseLT(); Ib4(X0, X1, X2, X3);
+            X0 ^= wKey[112]; X1 ^= wKey[113]; X2 ^= wKey[114]; X3 ^= wKey[115];
+            InverseLT(); Ib3(X0, X1, X2, X3);
+            X0 ^= wKey[108]; X1 ^= wKey[109]; X2 ^= wKey[110]; X3 ^= wKey[111];
+            InverseLT(); Ib2(X0, X1, X2, X3);
+            X0 ^= wKey[104]; X1 ^= wKey[105]; X2 ^= wKey[106]; X3 ^= wKey[107];
+            InverseLT(); Ib1(X0, X1, X2, X3);
+            X0 ^= wKey[100]; X1 ^= wKey[101]; X2 ^= wKey[102]; X3 ^= wKey[103];
+            InverseLT(); Ib0(X0, X1, X2, X3);
+            X0 ^= wKey[96]; X1 ^= wKey[97]; X2 ^= wKey[98]; X3 ^= wKey[99];
+            InverseLT(); Ib7(X0, X1, X2, X3);
+            X0 ^= wKey[92]; X1 ^= wKey[93]; X2 ^= wKey[94]; X3 ^= wKey[95];
+            InverseLT(); Ib6(X0, X1, X2, X3);
+            X0 ^= wKey[88]; X1 ^= wKey[89]; X2 ^= wKey[90]; X3 ^= wKey[91];
+            InverseLT(); Ib5(X0, X1, X2, X3);
+            X0 ^= wKey[84]; X1 ^= wKey[85]; X2 ^= wKey[86]; X3 ^= wKey[87];
+            InverseLT(); Ib4(X0, X1, X2, X3);
+            X0 ^= wKey[80]; X1 ^= wKey[81]; X2 ^= wKey[82]; X3 ^= wKey[83];
+            InverseLT(); Ib3(X0, X1, X2, X3);
+            X0 ^= wKey[76]; X1 ^= wKey[77]; X2 ^= wKey[78]; X3 ^= wKey[79];
+            InverseLT(); Ib2(X0, X1, X2, X3);
+            X0 ^= wKey[72]; X1 ^= wKey[73]; X2 ^= wKey[74]; X3 ^= wKey[75];
+            InverseLT(); Ib1(X0, X1, X2, X3);
+            X0 ^= wKey[68]; X1 ^= wKey[69]; X2 ^= wKey[70]; X3 ^= wKey[71];
+            InverseLT(); Ib0(X0, X1, X2, X3);
+            X0 ^= wKey[64]; X1 ^= wKey[65]; X2 ^= wKey[66]; X3 ^= wKey[67];
+            InverseLT(); Ib7(X0, X1, X2, X3);
+            X0 ^= wKey[60]; X1 ^= wKey[61]; X2 ^= wKey[62]; X3 ^= wKey[63];
+            InverseLT(); Ib6(X0, X1, X2, X3);
+            X0 ^= wKey[56]; X1 ^= wKey[57]; X2 ^= wKey[58]; X3 ^= wKey[59];
+            InverseLT(); Ib5(X0, X1, X2, X3);
+            X0 ^= wKey[52]; X1 ^= wKey[53]; X2 ^= wKey[54]; X3 ^= wKey[55];
+            InverseLT(); Ib4(X0, X1, X2, X3);
+            X0 ^= wKey[48]; X1 ^= wKey[49]; X2 ^= wKey[50]; X3 ^= wKey[51];
+            InverseLT(); Ib3(X0, X1, X2, X3);
+            X0 ^= wKey[44]; X1 ^= wKey[45]; X2 ^= wKey[46]; X3 ^= wKey[47];
+            InverseLT(); Ib2(X0, X1, X2, X3);
+            X0 ^= wKey[40]; X1 ^= wKey[41]; X2 ^= wKey[42]; X3 ^= wKey[43];
+            InverseLT(); Ib1(X0, X1, X2, X3);
+            X0 ^= wKey[36]; X1 ^= wKey[37]; X2 ^= wKey[38]; X3 ^= wKey[39];
+            InverseLT(); Ib0(X0, X1, X2, X3);
+            X0 ^= wKey[32]; X1 ^= wKey[33]; X2 ^= wKey[34]; X3 ^= wKey[35];
+            InverseLT(); Ib7(X0, X1, X2, X3);
+            X0 ^= wKey[28]; X1 ^= wKey[29]; X2 ^= wKey[30]; X3 ^= wKey[31];
+            InverseLT(); Ib6(X0, X1, X2, X3);
+            X0 ^= wKey[24]; X1 ^= wKey[25]; X2 ^= wKey[26]; X3 ^= wKey[27];
+            InverseLT(); Ib5(X0, X1, X2, X3);
+            X0 ^= wKey[20]; X1 ^= wKey[21]; X2 ^= wKey[22]; X3 ^= wKey[23];
+            InverseLT(); Ib4(X0, X1, X2, X3);
+            X0 ^= wKey[16]; X1 ^= wKey[17]; X2 ^= wKey[18]; X3 ^= wKey[19];
+            InverseLT(); Ib3(X0, X1, X2, X3);
+            X0 ^= wKey[12]; X1 ^= wKey[13]; X2 ^= wKey[14]; X3 ^= wKey[15];
+            InverseLT(); Ib2(X0, X1, X2, X3);
+            X0 ^= wKey[8]; X1 ^= wKey[9]; X2 ^= wKey[10]; X3 ^= wKey[11];
+            InverseLT(); Ib1(X0, X1, X2, X3);
+            X0 ^= wKey[4]; X1 ^= wKey[5]; X2 ^= wKey[6]; X3 ^= wKey[7];
+            InverseLT(); Ib0(X0, X1, X2, X3);
+
+            Pack.UInt32_To_LE((uint)(X0 ^ wKey[0]), output);
+            Pack.UInt32_To_LE((uint)(X1 ^ wKey[1]), output[4..]);
+            Pack.UInt32_To_LE((uint)(X2 ^ wKey[2]), output[8..]);
+            Pack.UInt32_To_LE((uint)(X3 ^ wKey[3]), output[12..]);
+        }
+#else
         protected override void EncryptBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
             X0 = (int)Pack.LE_To_UInt32(input, inOff);
@@ -204,14 +320,6 @@ namespace Org.BouncyCastle.Crypto.Engines
             Pack.UInt32_To_LE((uint)(wKey[131] ^ X3), output, outOff + 12);
         }
 
-        /**
-        * Decrypt one block of ciphertext.
-        *
-        * @param input the array containing the input data.
-        * @param inOff offset into the in array the data starts at.
-        * @param output the array the output data will be copied into.
-        * @param outOff the offset into the out array the output will start at.
-        */
         protected override void DecryptBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
             X0 = wKey[128] ^ (int)Pack.LE_To_UInt32(input, inOff);
@@ -288,5 +396,6 @@ namespace Org.BouncyCastle.Crypto.Engines
             Pack.UInt32_To_LE((uint)(X2 ^ wKey[2]), output, outOff + 8);
             Pack.UInt32_To_LE((uint)(X3 ^ wKey[3]), output, outOff + 12);
         }
+#endif
     }
 }
diff --git a/crypto/src/crypto/engines/SerpentEngineBase.cs b/crypto/src/crypto/engines/SerpentEngineBase.cs
index 9de552233..8ddbc4b6f 100644
--- a/crypto/src/crypto/engines/SerpentEngineBase.cs
+++ b/crypto/src/crypto/engines/SerpentEngineBase.cs
@@ -75,6 +75,16 @@ namespace Org.BouncyCastle.Crypto.Engines
             Check.DataLength(input, inOff, BlockSize, "input buffer too short");
             Check.OutputLength(output, outOff, BlockSize, "output buffer too short");
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            if (encrypting)
+            {
+                EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+            }
+            else
+            {
+                DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+            }
+#else
             if (encrypting)
             {
                 EncryptBlock(input, inOff, output, outOff);
@@ -83,9 +93,32 @@ namespace Org.BouncyCastle.Crypto.Engines
             {
                 DecryptBlock(input, inOff, output, outOff);
             }
+#endif
+
+            return BlockSize;
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            if (wKey == null)
+                throw new InvalidOperationException(AlgorithmName + " not initialised");
+
+            Check.DataLength(input, BlockSize, "input buffer too short");
+            Check.OutputLength(output, BlockSize, "output buffer too short");
+
+            if (encrypting)
+            {
+                EncryptBlock(input, output);
+            }
+            else
+            {
+                DecryptBlock(input, output);
+            }
 
             return BlockSize;
         }
+#endif
 
         public virtual void Reset()
         {
@@ -462,8 +495,12 @@ namespace Org.BouncyCastle.Crypto.Engines
 
         protected abstract int[] MakeWorkingKey(byte[] key);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        protected abstract void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output);
+        protected abstract void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output);
+#else
         protected abstract void EncryptBlock(byte[] input, int inOff, byte[] output, int outOff);
-
         protected abstract void DecryptBlock(byte[] input, int inOff, byte[] output, int outOff);
+#endif
     }
 }
diff --git a/crypto/src/crypto/engines/SkipjackEngine.cs b/crypto/src/crypto/engines/SkipjackEngine.cs
index c90646cc4..e78111abd 100644
--- a/crypto/src/crypto/engines/SkipjackEngine.cs
+++ b/crypto/src/crypto/engines/SkipjackEngine.cs
@@ -87,11 +87,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return BLOCK_SIZE;
         }
 
-        public virtual int ProcessBlock(
-            byte[]	input,
-            int		inOff,
-            byte[]	output,
-            int		outOff)
+        public virtual int ProcessBlock(byte[] input, int inOff, byte[]	output, int outOff)
         {
             if (key1 == null)
                 throw new InvalidOperationException("SKIPJACK engine not initialised");
@@ -99,6 +95,16 @@ namespace Org.BouncyCastle.Crypto.Engines
             Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
             Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            if (encrypting)
+            {
+                EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+            }
+            else
+            {
+                DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+            }
+#else
             if (encrypting)
             {
                 EncryptBlock(input, inOff, output, outOff);
@@ -107,10 +113,33 @@ namespace Org.BouncyCastle.Crypto.Engines
             {
                 DecryptBlock(input, inOff, output, outOff);
             }
+#endif
 
 			return BLOCK_SIZE;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            if (key1 == null)
+                throw new InvalidOperationException("SKIPJACK engine not initialised");
+
+            Check.DataLength(input, BLOCK_SIZE, "input buffer too short");
+            Check.OutputLength(output, BLOCK_SIZE, "output buffer too short");
+
+            if (encrypting)
+            {
+                EncryptBlock(input, output);
+            }
+            else
+            {
+                DecryptBlock(input, output);
+            }
+
+            return BLOCK_SIZE;
+        }
+#endif
+
         public virtual void Reset()
         {
         }
@@ -135,11 +164,97 @@ namespace Org.BouncyCastle.Crypto.Engines
             return ((g5 << 8) + g6);
         }
 
-        public virtual int EncryptBlock(
-            byte[]      input,
-            int         inOff,
-            byte[]      outBytes,
-            int         outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            int w1 = (input[0] << 8) + (input[1] & 0xff);
+            int w2 = (input[2] << 8) + (input[3] & 0xff);
+            int w3 = (input[4] << 8) + (input[5] & 0xff);
+            int w4 = (input[6] << 8) + (input[7] & 0xff);
+
+            int k = 0;
+
+            for (int t = 0; t < 2; t++)
+            {
+                for (int i = 0; i < 8; i++)
+                {
+                    int tmp = w4;
+                    w4 = w3;
+                    w3 = w2;
+                    w2 = G(k, w1);
+                    w1 = w2 ^ tmp ^ (k + 1);
+                    k++;
+                }
+
+                for (int i = 0; i < 8; i++)
+                {
+                    int tmp = w4;
+                    w4 = w3;
+                    w3 = w1 ^ w2 ^ (k + 1);
+                    w2 = G(k, w1);
+                    w1 = tmp;
+                    k++;
+                }
+            }
+
+            output[0] = (byte)((w1 >> 8));
+            output[1] = (byte)(w1);
+            output[2] = (byte)((w2 >> 8));
+            output[3] = (byte)(w2);
+            output[4] = (byte)((w3 >> 8));
+            output[5] = (byte)(w3);
+            output[6] = (byte)((w4 >> 8));
+            output[7] = (byte)(w4);
+
+            return BLOCK_SIZE;
+        }
+
+        public virtual int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            int w2 = (input[0] << 8) + (input[1] & 0xff);
+            int w1 = (input[2] << 8) + (input[3] & 0xff);
+            int w4 = (input[4] << 8) + (input[5] & 0xff);
+            int w3 = (input[6] << 8) + (input[7] & 0xff);
+
+            int k = 31;
+
+            for (int t = 0; t < 2; t++)
+            {
+                for (int i = 0; i < 8; i++)
+                {
+                    int tmp = w4;
+                    w4 = w3;
+                    w3 = w2;
+                    w2 = H(k, w1);
+                    w1 = w2 ^ tmp ^ (k + 1);
+                    k--;
+                }
+
+                for (int i = 0; i < 8; i++)
+                {
+                    int tmp = w4;
+                    w4 = w3;
+                    w3 = w1 ^ w2 ^ (k + 1);
+                    w2 = H(k, w1);
+                    w1 = tmp;
+                    k--;
+                }
+            }
+
+            output[0] = (byte)((w2 >> 8));
+            output[1] = (byte)(w2);
+            output[2] = (byte)((w1 >> 8));
+            output[3] = (byte)(w1);
+            output[4] = (byte)((w4 >> 8));
+            output[5] = (byte)(w4);
+            output[6] = (byte)((w3 >> 8));
+            output[7] = (byte)(w3);
+
+            return BLOCK_SIZE;
+        }
+
+#else
+        public virtual int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
         {
             int w1 = (input[inOff + 0] << 8) + (input[inOff + 1] & 0xff);
             int w2 = (input[inOff + 2] << 8) + (input[inOff + 3] & 0xff);
@@ -183,31 +298,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return BLOCK_SIZE;
         }
 
-        /**
-        * the inverse of the G permutation.
-        */
-        private int H(
-            int     k,
-            int     w)
-        {
-            int h1, h2, h3, h4, h5, h6;
-
-            h1 = w & 0xff;
-            h2 = (w >> 8) & 0xff;
-
-            h3 = ftable[h2 ^ key3[k]] ^ h1;
-            h4 = ftable[h3 ^ key2[k]] ^ h2;
-            h5 = ftable[h4 ^ key1[k]] ^ h3;
-            h6 = ftable[h5 ^ key0[k]] ^ h4;
-
-            return ((h6 << 8) + h5);
-        }
-
-        public virtual int DecryptBlock(
-            byte[]      input,
-            int         inOff,
-            byte[]      outBytes,
-            int         outOff)
+        public virtual int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
         {
             int w2 = (input[inOff + 0] << 8) + (input[inOff + 1] & 0xff);
             int w1 = (input[inOff + 2] << 8) + (input[inOff + 3] & 0xff);
@@ -218,7 +309,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 
             for (int t = 0; t < 2; t++)
             {
-                for(int i = 0; i < 8; i++)
+                for (int i = 0; i < 8; i++)
                 {
                     int tmp = w4;
                     w4 = w3;
@@ -228,7 +319,7 @@ namespace Org.BouncyCastle.Crypto.Engines
                     k--;
                 }
 
-                for(int i = 0; i < 8; i++)
+                for (int i = 0; i < 8; i++)
                 {
                     int tmp = w4;
                     w4 = w3;
@@ -250,5 +341,22 @@ namespace Org.BouncyCastle.Crypto.Engines
 
             return BLOCK_SIZE;
         }
+#endif
+
+        /**
+        * the inverse of the G permutation.
+        */
+        private int H(int k, int w)
+        {
+            int h1 = w & 0xff;
+            int h2 = (w >> 8) & 0xff;
+
+            int h3 = ftable[h2 ^ key3[k]] ^ h1;
+            int h4 = ftable[h3 ^ key2[k]] ^ h2;
+            int h5 = ftable[h4 ^ key1[k]] ^ h3;
+            int h6 = ftable[h5 ^ key0[k]] ^ h4;
+
+            return (h6 << 8) + h5;
+        }
     }
 }
diff --git a/crypto/src/crypto/engines/TEAEngine.cs b/crypto/src/crypto/engines/TEAEngine.cs
index 7b700145e..bb6ae6dcc 100644
--- a/crypto/src/crypto/engines/TEAEngine.cs
+++ b/crypto/src/crypto/engines/TEAEngine.cs
@@ -60,11 +60,9 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @exception ArgumentException if the params argument is
 		* inappropriate.
 		*/
-        public virtual void Init(
-			bool				forEncryption,
-			ICipherParameters	parameters)
+        public virtual void Init(bool forEncryption, ICipherParameters parameters)
 		{
-			if (!(parameters is KeyParameter))
+			if (!(parameters is KeyParameter keyParameter))
 			{
 				throw new ArgumentException("invalid parameter passed to TEA init - "
 					+ Platform.GetTypeName(parameters));
@@ -73,16 +71,10 @@ namespace Org.BouncyCastle.Crypto.Engines
 			_forEncryption = forEncryption;
 			_initialised = true;
 
-			KeyParameter p = (KeyParameter) parameters;
-
-			setKey(p.GetKey());
+			SetKey(keyParameter.GetKey());
 		}
 
-        public virtual int ProcessBlock(
-			byte[]  inBytes,
-			int     inOff,
-			byte[]  outBytes,
-			int     outOff)
+        public virtual int ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff)
 		{
 			if (!_initialised)
 				throw new InvalidOperationException(AlgorithmName + " not initialised");
@@ -90,12 +82,33 @@ namespace Org.BouncyCastle.Crypto.Engines
             Check.DataLength(inBytes, inOff, block_size, "input buffer too short");
             Check.OutputLength(outBytes, outOff, block_size, "output buffer too short");
 
-            return _forEncryption
-				?	encryptBlock(inBytes, inOff, outBytes, outOff)
-				:	decryptBlock(inBytes, inOff, outBytes, outOff);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			return _forEncryption
+				? EncryptBlock(inBytes.AsSpan(inOff), outBytes.AsSpan(outOff))
+				: DecryptBlock(inBytes.AsSpan(inOff), outBytes.AsSpan(outOff));
+#else
+			return _forEncryption
+				? EncryptBlock(inBytes, inOff, outBytes, outOff)
+				: DecryptBlock(inBytes, inOff, outBytes, outOff);
+#endif
 		}
 
-        public virtual void Reset()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			if (!_initialised)
+				throw new InvalidOperationException(AlgorithmName + " not initialised");
+
+			Check.DataLength(input, block_size, "input buffer too short");
+			Check.OutputLength(output, block_size, "output buffer too short");
+
+			return _forEncryption
+				? EncryptBlock(input, output)
+				: DecryptBlock(input, output);
+		}
+#endif
+
+		public virtual void Reset()
 		{
 		}
 
@@ -104,8 +117,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		*
 		* @param  key  the key to be used
 		*/
-		private void setKey(
-			byte[] key)
+		private void SetKey(byte[] key)
 		{
 			_a = Pack.BE_To_UInt32(key, 0);
 			_b = Pack.BE_To_UInt32(key, 4);
@@ -113,18 +125,57 @@ namespace Org.BouncyCastle.Crypto.Engines
 			_d = Pack.BE_To_UInt32(key, 12);
 		}
 
-		private int encryptBlock(
-			byte[]	inBytes,
-			int		inOff,
-			byte[]	outBytes,
-			int		outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			// Pack bytes into integers
+			uint v0 = Pack.BE_To_UInt32(input);
+			uint v1 = Pack.BE_To_UInt32(input[4..]);
+
+			uint sum = 0;
+
+			for (int i = 0; i != rounds; i++)
+			{
+				sum += delta;
+				v0  += ((v1 << 4) + _a) ^ (v1 + sum) ^ ((v1 >> 5) + _b);
+				v1  += ((v0 << 4) + _c) ^ (v0 + sum) ^ ((v0 >> 5) + _d);
+			}
+
+			Pack.UInt32_To_BE(v0, output);
+			Pack.UInt32_To_BE(v1, output[4..]);
+
+			return block_size;
+		}
+
+		private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			// Pack bytes into integers
+			uint v0 = Pack.BE_To_UInt32(input);
+			uint v1 = Pack.BE_To_UInt32(input[4..]);
+
+			uint sum = d_sum;
+
+			for (int i = 0; i != rounds; i++)
+			{
+				v1  -= ((v0 << 4) + _c) ^ (v0 + sum) ^ ((v0 >> 5) + _d);
+				v0  -= ((v1 << 4) + _a) ^ (v1 + sum) ^ ((v1 >> 5) + _b);
+				sum -= delta;
+			}
+
+			Pack.UInt32_To_BE(v0, output);
+			Pack.UInt32_To_BE(v1, output[4..]);
+
+			return block_size;
+		}
+#else
+		private int EncryptBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff)
 		{
 			// Pack bytes into integers
 			uint v0 = Pack.BE_To_UInt32(inBytes, inOff);
 			uint v1 = Pack.BE_To_UInt32(inBytes, inOff + 4);
-	        
+
 			uint sum = 0;
-	        
+
 			for (int i = 0; i != rounds; i++)
 			{
 				sum += delta;
@@ -138,11 +189,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return block_size;
 		}
 
-		private int decryptBlock(
-			byte[]	inBytes,
-			int		inOff,
-			byte[]	outBytes,
-			int		outOff)
+		private int DecryptBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff)
 		{
 			// Pack bytes into integers
 			uint v0 = Pack.BE_To_UInt32(inBytes, inOff);
@@ -162,5 +209,6 @@ namespace Org.BouncyCastle.Crypto.Engines
 
 			return block_size;
 		}
+#endif
 	}
 }
diff --git a/crypto/src/crypto/engines/ThreefishEngine.cs b/crypto/src/crypto/engines/ThreefishEngine.cs
index c5aee5395..c22691fc2 100644
--- a/crypto/src/crypto/engines/ThreefishEngine.cs
+++ b/crypto/src/crypto/engines/ThreefishEngine.cs
@@ -285,15 +285,8 @@ namespace Org.BouncyCastle.Crypto.Engines
 
         public virtual int ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff)
 		{
-			if ((outOff + blocksizeBytes) > outBytes.Length)
-			{
-				throw new DataLengthException("Output buffer too short");
-			}
-
-			if ((inOff + blocksizeBytes) > inBytes.Length)
-			{
-				throw new DataLengthException("Input buffer too short");
-			}
+			Check.DataLength(inBytes, inOff, blocksizeBytes, "input buffer too short");
+			Check.OutputLength(outBytes, outOff, blocksizeBytes, "output buffer too short");
 
 			Pack.LE_To_UInt64(inBytes, inOff, currentBlock);
 			ProcessBlock(this.currentBlock, this.currentBlock);
@@ -301,6 +294,19 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return blocksizeBytes;
 		}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			Check.DataLength(input, blocksizeBytes, "input buffer too short");
+			Check.OutputLength(output, blocksizeBytes, "output buffer too short");
+
+			Pack.LE_To_UInt64(input, currentBlock);
+			ProcessBlock(this.currentBlock, this.currentBlock);
+			Pack.UInt64_To_LE(currentBlock, output);
+			return blocksizeBytes;
+		}
+#endif
+
 		/// <summary>
 		/// Process a block of data represented as 64 bit words.
 		/// </summary>
@@ -317,13 +323,9 @@ namespace Org.BouncyCastle.Crypto.Engines
 			}
 
 			if (inWords.Length != blocksizeWords)
-			{
-				throw new DataLengthException("Input buffer too short");
-			}
+				throw new DataLengthException("input buffer too short");
 			if (outWords.Length != blocksizeWords)
-			{
-				throw new DataLengthException("Output buffer too short");
-			}
+				throw new OutputLengthException("output buffer too short");
 
 			if (forEncryption)
 			{
diff --git a/crypto/src/crypto/engines/TnepresEngine.cs b/crypto/src/crypto/engines/TnepresEngine.cs
index ce687d1e5..cb008a182 100644
--- a/crypto/src/crypto/engines/TnepresEngine.cs
+++ b/crypto/src/crypto/engines/TnepresEngine.cs
@@ -157,14 +157,130 @@ namespace Org.BouncyCastle.Crypto.Engines
             return w;
         }
 
-        /**
-        * Encrypt one block of plaintext.
-        *
-        * @param input the array containing the input data.
-        * @param inOff offset into the in array the data starts at.
-        * @param output the array the output data will be copied into.
-        * @param outOff the offset into the out array the output will start at.
-        */
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        protected override void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            X3 = (int)Pack.BE_To_UInt32(input);
+            X2 = (int)Pack.BE_To_UInt32(input[4..]);
+            X1 = (int)Pack.BE_To_UInt32(input[8..]);
+            X0 = (int)Pack.BE_To_UInt32(input[12..]);
+
+            Sb0(wKey[0] ^ X0, wKey[1] ^ X1, wKey[2] ^ X2, wKey[3] ^ X3); LT();
+            Sb1(wKey[4] ^ X0, wKey[5] ^ X1, wKey[6] ^ X2, wKey[7] ^ X3); LT();
+            Sb2(wKey[8] ^ X0, wKey[9] ^ X1, wKey[10] ^ X2, wKey[11] ^ X3); LT();
+            Sb3(wKey[12] ^ X0, wKey[13] ^ X1, wKey[14] ^ X2, wKey[15] ^ X3); LT();
+            Sb4(wKey[16] ^ X0, wKey[17] ^ X1, wKey[18] ^ X2, wKey[19] ^ X3); LT();
+            Sb5(wKey[20] ^ X0, wKey[21] ^ X1, wKey[22] ^ X2, wKey[23] ^ X3); LT();
+            Sb6(wKey[24] ^ X0, wKey[25] ^ X1, wKey[26] ^ X2, wKey[27] ^ X3); LT();
+            Sb7(wKey[28] ^ X0, wKey[29] ^ X1, wKey[30] ^ X2, wKey[31] ^ X3); LT();
+            Sb0(wKey[32] ^ X0, wKey[33] ^ X1, wKey[34] ^ X2, wKey[35] ^ X3); LT();
+            Sb1(wKey[36] ^ X0, wKey[37] ^ X1, wKey[38] ^ X2, wKey[39] ^ X3); LT();
+            Sb2(wKey[40] ^ X0, wKey[41] ^ X1, wKey[42] ^ X2, wKey[43] ^ X3); LT();
+            Sb3(wKey[44] ^ X0, wKey[45] ^ X1, wKey[46] ^ X2, wKey[47] ^ X3); LT();
+            Sb4(wKey[48] ^ X0, wKey[49] ^ X1, wKey[50] ^ X2, wKey[51] ^ X3); LT();
+            Sb5(wKey[52] ^ X0, wKey[53] ^ X1, wKey[54] ^ X2, wKey[55] ^ X3); LT();
+            Sb6(wKey[56] ^ X0, wKey[57] ^ X1, wKey[58] ^ X2, wKey[59] ^ X3); LT();
+            Sb7(wKey[60] ^ X0, wKey[61] ^ X1, wKey[62] ^ X2, wKey[63] ^ X3); LT();
+            Sb0(wKey[64] ^ X0, wKey[65] ^ X1, wKey[66] ^ X2, wKey[67] ^ X3); LT();
+            Sb1(wKey[68] ^ X0, wKey[69] ^ X1, wKey[70] ^ X2, wKey[71] ^ X3); LT();
+            Sb2(wKey[72] ^ X0, wKey[73] ^ X1, wKey[74] ^ X2, wKey[75] ^ X3); LT();
+            Sb3(wKey[76] ^ X0, wKey[77] ^ X1, wKey[78] ^ X2, wKey[79] ^ X3); LT();
+            Sb4(wKey[80] ^ X0, wKey[81] ^ X1, wKey[82] ^ X2, wKey[83] ^ X3); LT();
+            Sb5(wKey[84] ^ X0, wKey[85] ^ X1, wKey[86] ^ X2, wKey[87] ^ X3); LT();
+            Sb6(wKey[88] ^ X0, wKey[89] ^ X1, wKey[90] ^ X2, wKey[91] ^ X3); LT();
+            Sb7(wKey[92] ^ X0, wKey[93] ^ X1, wKey[94] ^ X2, wKey[95] ^ X3); LT();
+            Sb0(wKey[96] ^ X0, wKey[97] ^ X1, wKey[98] ^ X2, wKey[99] ^ X3); LT();
+            Sb1(wKey[100] ^ X0, wKey[101] ^ X1, wKey[102] ^ X2, wKey[103] ^ X3); LT();
+            Sb2(wKey[104] ^ X0, wKey[105] ^ X1, wKey[106] ^ X2, wKey[107] ^ X3); LT();
+            Sb3(wKey[108] ^ X0, wKey[109] ^ X1, wKey[110] ^ X2, wKey[111] ^ X3); LT();
+            Sb4(wKey[112] ^ X0, wKey[113] ^ X1, wKey[114] ^ X2, wKey[115] ^ X3); LT();
+            Sb5(wKey[116] ^ X0, wKey[117] ^ X1, wKey[118] ^ X2, wKey[119] ^ X3); LT();
+            Sb6(wKey[120] ^ X0, wKey[121] ^ X1, wKey[122] ^ X2, wKey[123] ^ X3); LT();
+            Sb7(wKey[124] ^ X0, wKey[125] ^ X1, wKey[126] ^ X2, wKey[127] ^ X3);
+
+            Pack.UInt32_To_BE((uint)(wKey[131] ^ X3), output);
+            Pack.UInt32_To_BE((uint)(wKey[130] ^ X2), output[4..]);
+            Pack.UInt32_To_BE((uint)(wKey[129] ^ X1), output[8..]);
+            Pack.UInt32_To_BE((uint)(wKey[128] ^ X0), output[12..]);
+        }
+
+        protected override void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            X3 = wKey[131] ^ (int)Pack.BE_To_UInt32(input);
+            X2 = wKey[130] ^ (int)Pack.BE_To_UInt32(input[4..]);
+            X1 = wKey[129] ^ (int)Pack.BE_To_UInt32(input[8..]);
+            X0 = wKey[128] ^ (int)Pack.BE_To_UInt32(input[12..]);
+
+            Ib7(X0, X1, X2, X3);
+            X0 ^= wKey[124]; X1 ^= wKey[125]; X2 ^= wKey[126]; X3 ^= wKey[127];
+            InverseLT(); Ib6(X0, X1, X2, X3);
+            X0 ^= wKey[120]; X1 ^= wKey[121]; X2 ^= wKey[122]; X3 ^= wKey[123];
+            InverseLT(); Ib5(X0, X1, X2, X3);
+            X0 ^= wKey[116]; X1 ^= wKey[117]; X2 ^= wKey[118]; X3 ^= wKey[119];
+            InverseLT(); Ib4(X0, X1, X2, X3);
+            X0 ^= wKey[112]; X1 ^= wKey[113]; X2 ^= wKey[114]; X3 ^= wKey[115];
+            InverseLT(); Ib3(X0, X1, X2, X3);
+            X0 ^= wKey[108]; X1 ^= wKey[109]; X2 ^= wKey[110]; X3 ^= wKey[111];
+            InverseLT(); Ib2(X0, X1, X2, X3);
+            X0 ^= wKey[104]; X1 ^= wKey[105]; X2 ^= wKey[106]; X3 ^= wKey[107];
+            InverseLT(); Ib1(X0, X1, X2, X3);
+            X0 ^= wKey[100]; X1 ^= wKey[101]; X2 ^= wKey[102]; X3 ^= wKey[103];
+            InverseLT(); Ib0(X0, X1, X2, X3);
+            X0 ^= wKey[96]; X1 ^= wKey[97]; X2 ^= wKey[98]; X3 ^= wKey[99];
+            InverseLT(); Ib7(X0, X1, X2, X3);
+            X0 ^= wKey[92]; X1 ^= wKey[93]; X2 ^= wKey[94]; X3 ^= wKey[95];
+            InverseLT(); Ib6(X0, X1, X2, X3);
+            X0 ^= wKey[88]; X1 ^= wKey[89]; X2 ^= wKey[90]; X3 ^= wKey[91];
+            InverseLT(); Ib5(X0, X1, X2, X3);
+            X0 ^= wKey[84]; X1 ^= wKey[85]; X2 ^= wKey[86]; X3 ^= wKey[87];
+            InverseLT(); Ib4(X0, X1, X2, X3);
+            X0 ^= wKey[80]; X1 ^= wKey[81]; X2 ^= wKey[82]; X3 ^= wKey[83];
+            InverseLT(); Ib3(X0, X1, X2, X3);
+            X0 ^= wKey[76]; X1 ^= wKey[77]; X2 ^= wKey[78]; X3 ^= wKey[79];
+            InverseLT(); Ib2(X0, X1, X2, X3);
+            X0 ^= wKey[72]; X1 ^= wKey[73]; X2 ^= wKey[74]; X3 ^= wKey[75];
+            InverseLT(); Ib1(X0, X1, X2, X3);
+            X0 ^= wKey[68]; X1 ^= wKey[69]; X2 ^= wKey[70]; X3 ^= wKey[71];
+            InverseLT(); Ib0(X0, X1, X2, X3);
+            X0 ^= wKey[64]; X1 ^= wKey[65]; X2 ^= wKey[66]; X3 ^= wKey[67];
+            InverseLT(); Ib7(X0, X1, X2, X3);
+            X0 ^= wKey[60]; X1 ^= wKey[61]; X2 ^= wKey[62]; X3 ^= wKey[63];
+            InverseLT(); Ib6(X0, X1, X2, X3);
+            X0 ^= wKey[56]; X1 ^= wKey[57]; X2 ^= wKey[58]; X3 ^= wKey[59];
+            InverseLT(); Ib5(X0, X1, X2, X3);
+            X0 ^= wKey[52]; X1 ^= wKey[53]; X2 ^= wKey[54]; X3 ^= wKey[55];
+            InverseLT(); Ib4(X0, X1, X2, X3);
+            X0 ^= wKey[48]; X1 ^= wKey[49]; X2 ^= wKey[50]; X3 ^= wKey[51];
+            InverseLT(); Ib3(X0, X1, X2, X3);
+            X0 ^= wKey[44]; X1 ^= wKey[45]; X2 ^= wKey[46]; X3 ^= wKey[47];
+            InverseLT(); Ib2(X0, X1, X2, X3);
+            X0 ^= wKey[40]; X1 ^= wKey[41]; X2 ^= wKey[42]; X3 ^= wKey[43];
+            InverseLT(); Ib1(X0, X1, X2, X3);
+            X0 ^= wKey[36]; X1 ^= wKey[37]; X2 ^= wKey[38]; X3 ^= wKey[39];
+            InverseLT(); Ib0(X0, X1, X2, X3);
+            X0 ^= wKey[32]; X1 ^= wKey[33]; X2 ^= wKey[34]; X3 ^= wKey[35];
+            InverseLT(); Ib7(X0, X1, X2, X3);
+            X0 ^= wKey[28]; X1 ^= wKey[29]; X2 ^= wKey[30]; X3 ^= wKey[31];
+            InverseLT(); Ib6(X0, X1, X2, X3);
+            X0 ^= wKey[24]; X1 ^= wKey[25]; X2 ^= wKey[26]; X3 ^= wKey[27];
+            InverseLT(); Ib5(X0, X1, X2, X3);
+            X0 ^= wKey[20]; X1 ^= wKey[21]; X2 ^= wKey[22]; X3 ^= wKey[23];
+            InverseLT(); Ib4(X0, X1, X2, X3);
+            X0 ^= wKey[16]; X1 ^= wKey[17]; X2 ^= wKey[18]; X3 ^= wKey[19];
+            InverseLT(); Ib3(X0, X1, X2, X3);
+            X0 ^= wKey[12]; X1 ^= wKey[13]; X2 ^= wKey[14]; X3 ^= wKey[15];
+            InverseLT(); Ib2(X0, X1, X2, X3);
+            X0 ^= wKey[8]; X1 ^= wKey[9]; X2 ^= wKey[10]; X3 ^= wKey[11];
+            InverseLT(); Ib1(X0, X1, X2, X3);
+            X0 ^= wKey[4]; X1 ^= wKey[5]; X2 ^= wKey[6]; X3 ^= wKey[7];
+            InverseLT(); Ib0(X0, X1, X2, X3);
+
+            Pack.UInt32_To_BE((uint)(X3 ^ wKey[3]), output);
+            Pack.UInt32_To_BE((uint)(X2 ^ wKey[2]), output[4..]);
+            Pack.UInt32_To_BE((uint)(X1 ^ wKey[1]), output[8..]);
+            Pack.UInt32_To_BE((uint)(X0 ^ wKey[0]), output[12..]);
+        }
+#else
         protected override void EncryptBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
             X3 = (int)Pack.BE_To_UInt32(input, inOff);
@@ -211,14 +327,6 @@ namespace Org.BouncyCastle.Crypto.Engines
             Pack.UInt32_To_BE((uint)(wKey[128] ^ X0), output, outOff + 12);
         }
 
-        /**
-        * Decrypt one block of ciphertext.
-        *
-        * @param input the array containing the input data.
-        * @param inOff offset into the in array the data starts at.
-        * @param output the array the output data will be copied into.
-        * @param outOff the offset into the out array the output will start at.
-        */
         protected override void DecryptBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
             X3 = wKey[131] ^ (int)Pack.BE_To_UInt32(input, inOff);
@@ -295,5 +403,6 @@ namespace Org.BouncyCastle.Crypto.Engines
             Pack.UInt32_To_BE((uint)(X1 ^ wKey[1]), output, outOff + 8);
             Pack.UInt32_To_BE((uint)(X0 ^ wKey[0]), output, outOff + 12);
         }
+#endif
     }
 }
diff --git a/crypto/src/crypto/engines/TwofishEngine.cs b/crypto/src/crypto/engines/TwofishEngine.cs
index 0758451e4..cb3e35b0a 100644
--- a/crypto/src/crypto/engines/TwofishEngine.cs
+++ b/crypto/src/crypto/engines/TwofishEngine.cs
@@ -299,11 +299,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			get { return false; }
 		}
 
-		public int ProcessBlock(
-            byte[]	input,
-            int		inOff,
-            byte[]	output,
-            int		outOff)
+		public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
             if (workingKey == null)
                 throw new InvalidOperationException("Twofish not initialised");
@@ -311,6 +307,16 @@ namespace Org.BouncyCastle.Crypto.Engines
             Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
             Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            if (encrypting)
+            {
+                EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+            }
+            else
+            {
+                DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+            }
+#else
             if (encrypting)
             {
                 EncryptBlock(input, inOff, output, outOff);
@@ -319,9 +325,32 @@ namespace Org.BouncyCastle.Crypto.Engines
             {
                 DecryptBlock(input, inOff, output, outOff);
             }
+#endif
+
+            return BLOCK_SIZE;
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            if (workingKey == null)
+                throw new InvalidOperationException("Twofish not initialised");
+
+            Check.DataLength(input, BLOCK_SIZE, "input buffer too short");
+            Check.OutputLength(output, BLOCK_SIZE, "output buffer too short");
+
+            if (encrypting)
+            {
+                EncryptBlock(input, output);
+            }
+            else
+            {
+                DecryptBlock(input, output);
+            }
 
             return BLOCK_SIZE;
         }
+#endif
 
         public void Reset()
         {
@@ -424,6 +453,80 @@ namespace Org.BouncyCastle.Crypto.Engines
             */
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        /**
+        * Encrypt the given input starting at the given offset and place
+        * the result in the provided buffer starting at the given offset.
+        * The input will be an exact multiple of our blocksize.
+        *
+        * encryptBlock uses the pre-calculated gSBox[] and subKey[]
+        * arrays.
+        */
+        private void EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            int x0 = (int)Pack.LE_To_UInt32(input) ^ gSubKeys[INPUT_WHITEN];
+            int x1 = (int)Pack.LE_To_UInt32(input[4..]) ^ gSubKeys[INPUT_WHITEN + 1];
+            int x2 = (int)Pack.LE_To_UInt32(input[8..]) ^ gSubKeys[INPUT_WHITEN + 2];
+            int x3 = (int)Pack.LE_To_UInt32(input[12..]) ^ gSubKeys[INPUT_WHITEN + 3];
+
+            int k = ROUND_SUBKEYS;
+            int t0, t1;
+            for (int r = 0; r < ROUNDS; r +=2)
+            {
+                t0 = Fe32_0(x0);
+                t1 = Fe32_3(x1);
+                x2 ^= t0 + t1 + gSubKeys[k++];
+                x2 = Integers.RotateRight(x2, 1);
+                x3 = Integers.RotateLeft(x3, 1) ^ (t0 + 2*t1 + gSubKeys[k++]);
+
+                t0 = Fe32_0(x2);
+                t1 = Fe32_3(x3);
+                x0 ^= t0 + t1 + gSubKeys[k++];
+                x0 = Integers.RotateRight(x0, 1);
+                x1 = Integers.RotateLeft(x1, 1) ^ (t0 + 2*t1 + gSubKeys[k++]);
+            }
+
+            Pack.UInt32_To_LE((uint)(x2 ^ gSubKeys[OUTPUT_WHITEN]), output);
+            Pack.UInt32_To_LE((uint)(x3 ^ gSubKeys[OUTPUT_WHITEN + 1]), output[4..]);
+            Pack.UInt32_To_LE((uint)(x0 ^ gSubKeys[OUTPUT_WHITEN + 2]), output[8..]);
+            Pack.UInt32_To_LE((uint)(x1 ^ gSubKeys[OUTPUT_WHITEN + 3]), output[12..]);
+        }
+
+        /**
+        * Decrypt the given input starting at the given offset and place
+        * the result in the provided buffer starting at the given offset.
+        * The input will be an exact multiple of our blocksize.
+        */
+        private void DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            int x2 = (int)Pack.LE_To_UInt32(input) ^ gSubKeys[OUTPUT_WHITEN];
+            int x3 = (int)Pack.LE_To_UInt32(input[4..]) ^ gSubKeys[OUTPUT_WHITEN + 1];
+            int x0 = (int)Pack.LE_To_UInt32(input[8..]) ^ gSubKeys[OUTPUT_WHITEN + 2];
+            int x1 = (int)Pack.LE_To_UInt32(input[12..]) ^ gSubKeys[OUTPUT_WHITEN + 3];
+
+            int k = ROUND_SUBKEYS + 2 * ROUNDS -1 ;
+            int t0, t1;
+            for (int r = 0; r< ROUNDS ; r +=2)
+            {
+                t0 = Fe32_0(x2);
+                t1 = Fe32_3(x3);
+                x1 ^= t0 + 2*t1 + gSubKeys[k--];
+                x0 = Integers.RotateLeft(x0, 1) ^ (t0 + t1 + gSubKeys[k--]);
+                x1 = Integers.RotateRight(x1, 1);
+
+                t0 = Fe32_0(x0);
+                t1 = Fe32_3(x1);
+                x3 ^= t0 + 2*t1 + gSubKeys[k--];
+                x2 = Integers.RotateLeft(x2, 1) ^ (t0 + t1 + gSubKeys[k--]);
+                x3 = Integers.RotateRight(x3, 1);
+            }
+
+            Pack.UInt32_To_LE((uint)(x0 ^ gSubKeys[INPUT_WHITEN]), output);
+            Pack.UInt32_To_LE((uint)(x1 ^ gSubKeys[INPUT_WHITEN + 1]), output[4..]);
+            Pack.UInt32_To_LE((uint)(x2 ^ gSubKeys[INPUT_WHITEN + 2]), output[8..]);
+            Pack.UInt32_To_LE((uint)(x3 ^ gSubKeys[INPUT_WHITEN + 3]), output[12..]);
+        }
+#else
         /**
         * Encrypt the given input starting at the given offset and place
         * the result in the provided buffer starting at the given offset.
@@ -432,11 +535,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * encryptBlock uses the pre-calculated gSBox[] and subKey[]
         * arrays.
         */
-        private void EncryptBlock(
-            byte[] src,
-            int srcIndex,
-            byte[] dst,
-            int dstIndex)
+        private void EncryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex)
         {
             int x0 = (int)Pack.LE_To_UInt32(src, srcIndex) ^ gSubKeys[INPUT_WHITEN];
             int x1 = (int)Pack.LE_To_UInt32(src, srcIndex + 4) ^ gSubKeys[INPUT_WHITEN + 1];
@@ -471,11 +570,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * the result in the provided buffer starting at the given offset.
         * The input will be an exact multiple of our blocksize.
         */
-        private void DecryptBlock(
-            byte[] src,
-            int srcIndex,
-            byte[] dst,
-            int dstIndex)
+        private void DecryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex)
         {
             int x2 = (int)Pack.LE_To_UInt32(src, srcIndex) ^ gSubKeys[OUTPUT_WHITEN];
             int x3 = (int)Pack.LE_To_UInt32(src, srcIndex + 4) ^ gSubKeys[OUTPUT_WHITEN + 1];
@@ -484,7 +579,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 
             int k = ROUND_SUBKEYS + 2 * ROUNDS -1 ;
             int t0, t1;
-            for (int r = 0; r< ROUNDS ; r +=2)
+            for (int r = 0; r < ROUNDS ; r += 2)
             {
                 t0 = Fe32_0(x2);
                 t1 = Fe32_3(x3);
@@ -504,6 +599,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             Pack.UInt32_To_LE((uint)(x2 ^ gSubKeys[INPUT_WHITEN + 2]), dst, dstIndex + 8);
             Pack.UInt32_To_LE((uint)(x3 ^ gSubKeys[INPUT_WHITEN + 3]), dst, dstIndex + 12);
         }
+#endif
 
         /*
         * TODO:  This can be optimised and made cleaner by combining
diff --git a/crypto/src/crypto/engines/XTEAEngine.cs b/crypto/src/crypto/engines/XTEAEngine.cs
index 5fcfa4a57..e70498a5f 100644
--- a/crypto/src/crypto/engines/XTEAEngine.cs
+++ b/crypto/src/crypto/engines/XTEAEngine.cs
@@ -76,11 +76,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			setKey(p.GetKey());
 		}
 
-        public virtual int ProcessBlock(
-			byte[]	inBytes,
-			int		inOff,
-			byte[]	outBytes,
-			int		outOff)
+        public virtual int ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff)
 		{
 			if (!_initialised)
 				throw new InvalidOperationException(AlgorithmName + " not initialised");
@@ -88,12 +84,33 @@ namespace Org.BouncyCastle.Crypto.Engines
             Check.DataLength(inBytes, inOff, block_size, "input buffer too short");
             Check.OutputLength(outBytes, outOff, block_size, "output buffer too short");
 
-            return _forEncryption
-				?	encryptBlock(inBytes, inOff, outBytes, outOff)
-				:	decryptBlock(inBytes, inOff, outBytes, outOff);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			return _forEncryption
+				? EncryptBlock(inBytes.AsSpan(inOff), outBytes.AsSpan(outOff))
+				: DecryptBlock(inBytes.AsSpan(inOff), outBytes.AsSpan(outOff));
+#else
+			return _forEncryption
+				? EncryptBlock(inBytes, inOff, outBytes, outOff)
+				: DecryptBlock(inBytes, inOff, outBytes, outOff);
+#endif
 		}
 
-        public virtual void Reset()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+			if (!_initialised)
+				throw new InvalidOperationException(AlgorithmName + " not initialised");
+
+			Check.DataLength(input, block_size, "input buffer too short");
+			Check.OutputLength(output, block_size, "output buffer too short");
+
+			return _forEncryption
+				? EncryptBlock(input, output)
+				: DecryptBlock(input, output);
+		}
+#endif
+
+		public virtual void Reset()
 		{
 		}
 
@@ -119,13 +136,45 @@ namespace Org.BouncyCastle.Crypto.Engines
 			}
 		}
 
-		private int encryptBlock(
-			byte[]  inBytes,
-			int     inOff,
-			byte[]  outBytes,
-			int     outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
 		{
 			// Pack bytes into integers
+			uint v0 = Pack.BE_To_UInt32(input);
+			uint v1 = Pack.BE_To_UInt32(input[4..]);
+
+			for (int i = 0; i < rounds; i++)
+			{
+				v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ _sum0[i];
+				v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ _sum1[i];
+			}
+
+			Pack.UInt32_To_BE(v0, output);
+			Pack.UInt32_To_BE(v1, output[4..]);
+
+			return block_size;
+		}
+
+		private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			// Pack bytes into integers
+			uint v0 = Pack.BE_To_UInt32(input);
+			uint v1 = Pack.BE_To_UInt32(input[4..]);
+
+			for (int i = rounds - 1; i >= 0; i--)
+			{
+				v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ _sum1[i];
+				v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ _sum0[i];
+			}
+
+			Pack.UInt32_To_BE(v0, output);
+			Pack.UInt32_To_BE(v1, output[4..]);
+
+			return block_size;
+		}
+#else
+		private int EncryptBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff)
+		{
 			uint v0 = Pack.BE_To_UInt32(inBytes, inOff);
 			uint v1 = Pack.BE_To_UInt32(inBytes, inOff + 4);
 
@@ -141,11 +190,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return block_size;
 		}
 
-		private int decryptBlock(
-			byte[]	inBytes,
-			int		inOff,
-			byte[]	outBytes,
-			int		outOff)
+		private int DecryptBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff)
 		{
 			// Pack bytes into integers
 			uint v0 = Pack.BE_To_UInt32(inBytes, inOff);
@@ -162,5 +207,6 @@ namespace Org.BouncyCastle.Crypto.Engines
 
 			return block_size;
 		}
+#endif
 	}
 }
diff --git a/crypto/src/crypto/macs/CMac.cs b/crypto/src/crypto/macs/CMac.cs
index 682c12bac..342dbd93d 100644
--- a/crypto/src/crypto/macs/CMac.cs
+++ b/crypto/src/crypto/macs/CMac.cs
@@ -132,8 +132,7 @@ namespace Org.BouncyCastle.Crypto.Macs
             return ret;
         }
 
-        public void Init(
-            ICipherParameters parameters)
+        public void Init(ICipherParameters parameters)
         {
             if (parameters is KeyParameter)
             {
@@ -159,8 +158,7 @@ namespace Org.BouncyCastle.Crypto.Macs
             return macSize;
         }
 
-        public void Update(
-            byte input)
+        public void Update(byte input)
         {
             if (bufOff == buf.Length)
             {
@@ -171,14 +169,14 @@ namespace Org.BouncyCastle.Crypto.Macs
             buf[bufOff++] = input;
         }
 
-        public void BlockUpdate(
-            byte[]	inBytes,
-            int		inOff,
-            int		len)
+        public void BlockUpdate(byte[] inBytes, int inOff, int len)
         {
             if (len < 0)
                 throw new ArgumentException("Can't have a negative input length!");
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            BlockUpdate(inBytes.AsSpan(inOff, len));
+#else
             int blockSize = cipher.GetBlockSize();
             int gapLen = blockSize - bufOff;
 
@@ -204,13 +202,43 @@ namespace Org.BouncyCastle.Crypto.Macs
             Array.Copy(inBytes, inOff, buf, bufOff, len);
 
             bufOff += len;
+#endif
         }
 
-        public int DoFinal(
-            byte[]	outBytes,
-            int		outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
         {
             int blockSize = cipher.GetBlockSize();
+            int gapLen = blockSize - bufOff;
+
+            if (input.Length > gapLen)
+            {
+                input[..gapLen].CopyTo(buf.AsSpan(bufOff));
+
+                cipher.ProcessBlock(buf, mac);
+
+                bufOff = 0;
+                input = input[gapLen..];
+
+                while (input.Length > blockSize)
+                {
+                    cipher.ProcessBlock(input, mac);
+                    input = input[blockSize..];
+                }
+            }
+
+            input.CopyTo(buf.AsSpan(bufOff));
+
+            bufOff += input.Length;
+        }
+#endif
+
+        public int DoFinal(byte[] outBytes, int outOff)
+        {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return DoFinal(outBytes.AsSpan(outOff));
+#else
+            int blockSize = cipher.GetBlockSize();
 
             byte[] lu;
             if (bufOff == blockSize)
@@ -235,7 +263,39 @@ namespace Org.BouncyCastle.Crypto.Macs
             Reset();
 
             return macSize;
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int DoFinal(Span<byte> output)
+        {
+            int blockSize = cipher.GetBlockSize();
+
+            byte[] lu;
+            if (bufOff == blockSize)
+            {
+                lu = Lu;
+            }
+            else
+            {
+                new ISO7816d4Padding().AddPadding(buf, bufOff);
+                lu = Lu2;
+            }
+
+            for (int i = 0; i < mac.Length; i++)
+            {
+                buf[i] ^= lu[i];
+            }
+
+            cipher.ProcessBlock(buf, mac);
+
+            mac.AsSpan(0, macSize).CopyTo(output);
+
+            Reset();
+
+            return macSize;
         }
+#endif
 
         /**
         * Reset the mac generator.
diff --git a/crypto/src/crypto/macs/CbcBlockCipherMac.cs b/crypto/src/crypto/macs/CbcBlockCipherMac.cs
index 146e16aa8..abf06170c 100644
--- a/crypto/src/crypto/macs/CbcBlockCipherMac.cs
+++ b/crypto/src/crypto/macs/CbcBlockCipherMac.cs
@@ -99,8 +99,7 @@ namespace Org.BouncyCastle.Crypto.Macs
             get { return cipher.AlgorithmName; }
         }
 
-		public void Init(
-            ICipherParameters parameters)
+		public void Init(ICipherParameters parameters)
         {
             Reset();
 
@@ -112,8 +111,7 @@ namespace Org.BouncyCastle.Crypto.Macs
             return macSize;
         }
 
-		public void Update(
-            byte input)
+		public void Update(byte input)
         {
 			if (bufOff == buf.Length)
             {
@@ -124,15 +122,15 @@ namespace Org.BouncyCastle.Crypto.Macs
 			buf[bufOff++] = input;
         }
 
-        public void BlockUpdate(
-            byte[]	input,
-            int		inOff,
-            int		len)
+        public void BlockUpdate(byte[] input, int inOff, int len)
         {
             if (len < 0)
                 throw new ArgumentException("Can't have a negative input length!");
 
-			int blockSize = cipher.GetBlockSize();
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            BlockUpdate(input.AsSpan(inOff, len));
+#else
+            int blockSize = cipher.GetBlockSize();
             int gapLen = blockSize - bufOff;
 
             if (len > gapLen)
@@ -157,13 +155,43 @@ namespace Org.BouncyCastle.Crypto.Macs
             Array.Copy(input, inOff, buf, bufOff, len);
 
             bufOff += len;
+#endif
         }
 
-        public int DoFinal(
-            byte[]	output,
-            int		outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
         {
             int blockSize = cipher.GetBlockSize();
+            int gapLen = blockSize - bufOff;
+
+            if (input.Length > gapLen)
+            {
+                input[..gapLen].CopyTo(buf.AsSpan(bufOff));
+
+                cipher.ProcessBlock(buf, buf);
+
+                bufOff = 0;
+                input = input[gapLen..];
+
+                while (input.Length > blockSize)
+                {
+                    cipher.ProcessBlock(input, buf);
+                    input = input[blockSize..];
+                }
+            }
+
+            input.CopyTo(buf.AsSpan(bufOff));
+
+            bufOff += input.Length;
+        }
+#endif
+
+        public int DoFinal(byte[] output, int outOff)
+        {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return DoFinal(output.AsSpan(outOff));
+#else
+            int blockSize = cipher.GetBlockSize();
 
             if (padding == null)
             {
@@ -191,9 +219,44 @@ namespace Org.BouncyCastle.Crypto.Macs
 			Reset();
 
 			return macSize;
+#endif
         }
 
-		/**
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int DoFinal(Span<byte> output)
+        {
+            int blockSize = cipher.GetBlockSize();
+
+            if (padding == null)
+            {
+                // pad with zeroes
+                while (bufOff < blockSize)
+                {
+                    buf[bufOff++] = 0;
+                }
+            }
+            else
+            {
+                if (bufOff == blockSize)
+                {
+                    cipher.ProcessBlock(buf, buf);
+                    bufOff = 0;
+                }
+
+				padding.AddPadding(buf, bufOff);
+            }
+
+			cipher.ProcessBlock(buf, buf);
+
+            buf.AsSpan(0, macSize).CopyTo(output);
+
+			Reset();
+
+			return macSize;
+        }
+#endif
+
+        /**
         * Reset the mac generator.
         */
         public void Reset()
diff --git a/crypto/src/crypto/macs/CfbBlockCipherMac.cs b/crypto/src/crypto/macs/CfbBlockCipherMac.cs
index 364cf8499..a4d005700 100644
--- a/crypto/src/crypto/macs/CfbBlockCipherMac.cs
+++ b/crypto/src/crypto/macs/CfbBlockCipherMac.cs
@@ -9,7 +9,7 @@ namespace Org.BouncyCastle.Crypto.Macs
     /**
     * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
     */
-    class MacCFBBlockCipher
+    internal class MacCfbBlockCipher
 		: IBlockCipher
     {
         private byte[] IV;
@@ -26,7 +26,7 @@ namespace Org.BouncyCastle.Crypto.Macs
         * feedback mode.
         * @param blockSize the block size in bits (note: a multiple of 8)
         */
-        public MacCFBBlockCipher(
+        public MacCfbBlockCipher(
             IBlockCipher	cipher,
             int				bitBlockSize)
         {
@@ -47,13 +47,10 @@ namespace Org.BouncyCastle.Crypto.Macs
         * @exception ArgumentException if the parameters argument is
         * inappropriate.
         */
-		public void Init(
-			bool				forEncryption,
-            ICipherParameters	parameters)
+		public void Init(bool forEncryption, ICipherParameters parameters)
         {
-			if (parameters is ParametersWithIV)
+			if (parameters is ParametersWithIV ivParam)
             {
-                ParametersWithIV ivParam = (ParametersWithIV)parameters;
                 byte[] iv = ivParam.GetIV();
 
                 if (iv.Length < IV.Length)
@@ -99,30 +96,10 @@ namespace Org.BouncyCastle.Crypto.Macs
             return blockSize;
         }
 
-		/**
-        * Process one block of input from the array in and write it to
-        * the out array.
-        *
-        * @param in the array containing the input data.
-        * @param inOff offset into the in array the data starts at.
-        * @param out the array the output data will be copied into.
-        * @param outOff the offset into the out array the output will start at.
-        * @exception DataLengthException if there isn't enough data in in, or
-        * space in out.
-        * @exception InvalidOperationException if the cipher isn't initialised.
-        * @return the number of bytes processed and produced.
-        */
-        public int ProcessBlock(
-            byte[]	input,
-            int		inOff,
-            byte[]	outBytes,
-            int		outOff)
+        public int ProcessBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
         {
-            if ((inOff + blockSize) > input.Length)
-                throw new DataLengthException("input buffer too short");
-
-			if ((outOff + blockSize) > outBytes.Length)
-                throw new DataLengthException("output buffer too short");
+            Check.DataLength(input, inOff, blockSize, "input buffer too short");
+            Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
 
 			cipher.ProcessBlock(cfbV, 0, cfbOutV, 0);
 
@@ -143,7 +120,33 @@ namespace Org.BouncyCastle.Crypto.Macs
 			return blockSize;
         }
 
-		/**
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            Check.DataLength(input, blockSize, "input buffer too short");
+            Check.OutputLength(output, blockSize, "output buffer too short");
+
+            cipher.ProcessBlock(cfbV, cfbOutV);
+
+            //
+            // XOR the cfbV with the plaintext producing the cipher text
+            //
+            for (int i = 0; i < blockSize; i++)
+            {
+                output[i] = (byte)(cfbOutV[i] ^ input[i]);
+            }
+
+            //
+            // change over the input block.
+            //
+            Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize);
+            output[..blockSize].CopyTo(cfbV.AsSpan(cfbV.Length - blockSize));
+
+            return blockSize;
+        }
+#endif
+
+        /**
         * reset the chaining vector back to the IV and reset the underlying
         * cipher.
         */
@@ -167,7 +170,7 @@ namespace Org.BouncyCastle.Crypto.Macs
         private byte[] mac;
         private byte[] Buffer;
         private int bufOff;
-        private MacCFBBlockCipher cipher;
+        private MacCfbBlockCipher cipher;
         private IBlockCipherPadding padding;
         private int macSize;
 
@@ -247,7 +250,7 @@ namespace Org.BouncyCastle.Crypto.Macs
 
 			mac = new byte[cipher.GetBlockSize()];
 
-			this.cipher = new MacCFBBlockCipher(cipher, cfbBitSize);
+			this.cipher = new MacCfbBlockCipher(cipher, cfbBitSize);
             this.padding = padding;
             this.macSize = macSizeInBits / 8;
 
@@ -260,8 +263,7 @@ namespace Org.BouncyCastle.Crypto.Macs
             get { return cipher.AlgorithmName; }
         }
 
-		public void Init(
-            ICipherParameters parameters)
+		public void Init(ICipherParameters parameters)
         {
             Reset();
 
@@ -273,8 +275,7 @@ namespace Org.BouncyCastle.Crypto.Macs
             return macSize;
         }
 
-		public void Update(
-            byte input)
+		public void Update(byte input)
         {
             if (bufOff == Buffer.Length)
             {
@@ -285,15 +286,15 @@ namespace Org.BouncyCastle.Crypto.Macs
 			Buffer[bufOff++] = input;
         }
 
-		public void BlockUpdate(
-            byte[]	input,
-            int		inOff,
-            int		len)
+		public void BlockUpdate(byte[] input, int inOff, int len)
         {
             if (len < 0)
                 throw new ArgumentException("Can't have a negative input length!");
 
-			int blockSize = cipher.GetBlockSize();
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            BlockUpdate(input.AsSpan(inOff, len));
+#else
+            int blockSize = cipher.GetBlockSize();
             int resultLen = 0;
             int gapLen = blockSize - bufOff;
 
@@ -319,13 +320,44 @@ namespace Org.BouncyCastle.Crypto.Macs
 			Array.Copy(input, inOff, Buffer, bufOff, len);
 
 			bufOff += len;
+#endif
         }
 
-		public int DoFinal(
-            byte[]	output,
-            int		outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
         {
             int blockSize = cipher.GetBlockSize();
+            int resultLen = 0;
+            int gapLen = blockSize - bufOff;
+
+            if (input.Length > gapLen)
+            {
+                input[..gapLen].CopyTo(Buffer.AsSpan(bufOff));
+
+                resultLen += cipher.ProcessBlock(Buffer, mac);
+
+                bufOff = 0;
+                input = input[gapLen..];
+
+                while (input.Length > blockSize)
+                {
+                    resultLen += cipher.ProcessBlock(input, mac);
+                    input = input[blockSize..];
+                }
+            }
+
+            input.CopyTo(Buffer.AsSpan(bufOff));
+
+            bufOff += input.Length;
+        }
+#endif
+
+        public int DoFinal(byte[] output, int outOff)
+        {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return DoFinal(output.AsSpan(outOff));
+#else
+            int blockSize = cipher.GetBlockSize();
 
             // pad with zeroes
             if (this.padding == null)
@@ -349,8 +381,39 @@ namespace Org.BouncyCastle.Crypto.Macs
 			Reset();
 
 			return macSize;
+#endif
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int DoFinal(Span<byte> output)
+        {
+            int blockSize = cipher.GetBlockSize();
+
+            // pad with zeroes
+            if (this.padding == null)
+            {
+                while (bufOff < blockSize)
+                {
+                    Buffer[bufOff++] = 0;
+                }
+            }
+            else
+            {
+                padding.AddPadding(Buffer, bufOff);
+            }
+
+            cipher.ProcessBlock(Buffer, 0, mac, 0);
+
+            cipher.GetMacBlock(mac);
+
+            mac.AsSpan(0, macSize).CopyTo(output);
+
+            Reset();
+
+            return macSize;
+        }
+#endif
+
         /**
         * Reset the mac generator.
         */
@@ -364,5 +427,4 @@ namespace Org.BouncyCastle.Crypto.Macs
             cipher.Reset();
         }
     }
-
 }
diff --git a/crypto/src/crypto/macs/DSTU7564Mac.cs b/crypto/src/crypto/macs/DSTU7564Mac.cs
index 36e86418a..401d85a1e 100644
--- a/crypto/src/crypto/macs/DSTU7564Mac.cs
+++ b/crypto/src/crypto/macs/DSTU7564Mac.cs
@@ -61,7 +61,7 @@ namespace Org.BouncyCastle.Crypto.Macs
 
         public void BlockUpdate(byte[] input, int inOff, int len)
         {
-            Check.DataLength(input, inOff, len, "Input buffer too short");
+            Check.DataLength(input, inOff, len, "input buffer too short");
 
             if (paddedKey == null)
                 throw new InvalidOperationException(AlgorithmName + " not initialised");
@@ -70,6 +70,17 @@ namespace Org.BouncyCastle.Crypto.Macs
             inputLength += (ulong)len;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            if (paddedKey == null)
+                throw new InvalidOperationException(AlgorithmName + " not initialised");
+
+            engine.BlockUpdate(input);
+            inputLength += (ulong)input.Length;
+        }
+#endif
+
         public void Update(byte input)
         {
             engine.Update(input);
@@ -78,11 +89,11 @@ namespace Org.BouncyCastle.Crypto.Macs
 
         public int DoFinal(byte[] output, int outOff)
         {
-            Check.OutputLength(output, outOff, macSize, "Output buffer too short");
-
             if (paddedKey == null)
                 throw new InvalidOperationException(AlgorithmName + " not initialised");
 
+            Check.OutputLength(output, outOff, macSize, "output buffer too short");
+
             Pad();
 
             engine.BlockUpdate(invertedKey, 0, invertedKey.Length);
@@ -92,6 +103,24 @@ namespace Org.BouncyCastle.Crypto.Macs
             return engine.DoFinal(output, outOff);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int DoFinal(Span<byte> output)
+        {
+            if (paddedKey == null)
+                throw new InvalidOperationException(AlgorithmName + " not initialised");
+
+            Check.OutputLength(output, macSize, "output buffer too short");
+
+            Pad();
+
+            engine.BlockUpdate(invertedKey);
+
+            inputLength = 0;
+
+            return engine.DoFinal(output);
+        }
+#endif
+
         public void Reset()
         {
             inputLength = 0;
diff --git a/crypto/src/crypto/macs/DSTU7624Mac.cs b/crypto/src/crypto/macs/DSTU7624Mac.cs
index 953d8164f..8fecb1915 100644
--- a/crypto/src/crypto/macs/DSTU7624Mac.cs
+++ b/crypto/src/crypto/macs/DSTU7624Mac.cs
@@ -1,87 +1,89 @@
 using System;
 
-using Org.BouncyCastle.Utilities;
-using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Engines;
 using Org.BouncyCastle.Crypto.Parameters;
-
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Macs
 {
-     /**
+    /**
      * implementation of DSTU 7624 MAC
      */
-     public class Dstu7624Mac : IMac
-     {
-          private int macSize;
-                    
-          private Dstu7624Engine engine;
-          private int blockSize;
+    public class Dstu7624Mac : IMac
+    {
+        private int macSize;
+
+        private Dstu7624Engine engine;
+        private int blockSize;
+
+        private byte[] c, cTemp, kDelta;
+        private byte[] buf;
+        private int bufOff;
 
-          private byte[] c, cTemp, kDelta;
-          private byte[] buf;
-          private int bufOff;
+        public Dstu7624Mac(int blockSizeBits, int q)
+        {
+            engine = new Dstu7624Engine(blockSizeBits);
+
+            blockSize = blockSizeBits / 8;
 
-          public Dstu7624Mac(int blockSizeBits, int q)
-          {
-               engine = new Dstu7624Engine(blockSizeBits);
+            macSize = q / 8;
 
-               blockSize = blockSizeBits / 8;
+            c = new byte[blockSize];
 
-               macSize = q / 8;
+            cTemp = new byte[blockSize];
 
-               c = new byte[blockSize];
-              
-               cTemp = new byte[blockSize];
+            kDelta = new byte[blockSize];
+            buf = new byte[blockSize];
+        }
+
+        public void Init(ICipherParameters parameters)
+        {
+            if (parameters is KeyParameter)
+            {
+                engine.Init(true, (KeyParameter)parameters);
+
+                engine.ProcessBlock(kDelta, 0, kDelta, 0);
+            }
+            else
+            {
+                throw new ArgumentException("invalid parameter passed to Dstu7624Mac init - "
+                + Platform.GetTypeName(parameters));
+            }
+        }
+
+        public string AlgorithmName
+        {
+            get { return "Dstu7624Mac"; }
+        }
 
-               kDelta = new byte[blockSize];
-               buf = new byte[blockSize];
+        public int GetMacSize()
+        {
+            return macSize;
         }
 
-          public void Init(ICipherParameters parameters)
-          {
-               if (parameters is KeyParameter)
-               {
-                    engine.Init(true, (KeyParameter)parameters);
-
-                    engine.ProcessBlock(kDelta, 0, kDelta, 0);
-               }
-               else
-               {
-                    throw new ArgumentException("invalid parameter passed to Dstu7624Mac init - "
-                    + Platform.GetTypeName(parameters));
-               }             
-          }
-
-          public string AlgorithmName
-          {
-               get { return "Dstu7624Mac"; }
-          }
-
-          public int GetMacSize()
-          {
-               return macSize;
-          }
-
-          public void Update(byte input)
-          {
+        public void Update(byte input)
+        {
             if (bufOff == buf.Length)
             {
-                processBlock(buf, 0);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                ProcessBlock(buf);
+#else
+                ProcessBlock(buf, 0);
+#endif
                 bufOff = 0;
             }
 
             buf[bufOff++] = input;
         }
 
-          public void BlockUpdate(byte[] input, int inOff, int len)
-          {
+        public void BlockUpdate(byte[] input, int inOff, int len)
+        {
             if (len < 0)
-            {
-                throw new ArgumentException(
-                    "Can't have a negative input length!");
-            }
+                throw new ArgumentException("Can't have a negative input length!");
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            BlockUpdate(input.AsSpan(inOff, len));
+#else
             int blockSize = engine.GetBlockSize();
             int gapLen = blockSize - bufOff;
 
@@ -89,7 +91,7 @@ namespace Org.BouncyCastle.Crypto.Macs
             {
                 Array.Copy(input, inOff, buf, bufOff, gapLen);
 
-                processBlock(buf, 0);
+                ProcessBlock(buf, 0);
 
                 bufOff = 0;
                 len -= gapLen;
@@ -97,7 +99,7 @@ namespace Org.BouncyCastle.Crypto.Macs
 
                 while (len > blockSize)
                 {
-                    processBlock(input, inOff);
+                    ProcessBlock(input, inOff);
 
                     len -= blockSize;
                     inOff += blockSize;
@@ -107,47 +109,107 @@ namespace Org.BouncyCastle.Crypto.Macs
             Array.Copy(input, inOff, buf, bufOff, len);
 
             bufOff += len;
+#endif
         }
 
-        private void processBlock(byte[] input, int inOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            int blockSize = engine.GetBlockSize();
+            int gapLen = blockSize - bufOff;
+
+            if (input.Length > gapLen)
+            {
+                input[..gapLen].CopyTo(buf.AsSpan(bufOff));
+
+                ProcessBlock(buf);
+
+                bufOff = 0;
+                input = input[gapLen..];
+
+                while (input.Length > blockSize)
+                {
+                    ProcessBlock(input);
+                    input = input[blockSize..];
+                }
+            }
+
+            input.CopyTo(buf.AsSpan(bufOff));
+
+            bufOff += input.Length;
+        }
+#endif
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void ProcessBlock(ReadOnlySpan<byte> input)
+        {
+            Xor(c, input, cTemp);
+
+            engine.ProcessBlock(cTemp, c);
+        }
+
+        private void Xor(ReadOnlySpan<byte> c, ReadOnlySpan<byte> input, Span<byte> xorResult)
+        {
+            for (int byteIndex = 0; byteIndex < blockSize; byteIndex++)
+            {
+                xorResult[byteIndex] = (byte)(c[byteIndex] ^ input[byteIndex]);
+            }
+        }
+#else
+        private void ProcessBlock(byte[] input, int inOff)
         {
             Xor(c, 0, input, inOff, cTemp);
 
             engine.ProcessBlock(cTemp, 0, c, 0);
         }
+#endif
 
         private void Xor(byte[] c, int cOff, byte[] input, int inOff, byte[] xorResult)
-          {
-               for (int byteIndex = 0; byteIndex < blockSize; byteIndex++)
-               {
-                    xorResult[byteIndex] = (byte)(c[byteIndex + cOff] ^ input[byteIndex + inOff]);
-               }
-          }
-
-          public int DoFinal(byte[] output, int outOff)
-          {
-            if (bufOff % buf.Length != 0)
+        {
+            for (int byteIndex = 0; byteIndex < blockSize; byteIndex++)
             {
-                throw new DataLengthException("Input must be a multiple of blocksize");
+                xorResult[byteIndex] = (byte)(c[byteIndex + cOff] ^ input[byteIndex + inOff]);
             }
+        }
+
+        public int DoFinal(byte[] output, int outOff)
+        {
+            if (bufOff % buf.Length != 0)
+                throw new DataLengthException("Input must be a multiple of blocksize");
+
+            Check.OutputLength(output, outOff, macSize, "output buffer too short");
 
             //Last block
             Xor(c, 0, buf, 0, cTemp);
             Xor(cTemp, 0, kDelta, 0, c);
             engine.ProcessBlock(c, 0, c, 0);
 
-            if (macSize + outOff > output.Length)
-            {
-                throw new DataLengthException("Output buffer too short");
-            }
-
             Array.Copy(c, 0, output, outOff, macSize);
 
             return macSize;
         }
 
-          public void Reset()
-          {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int DoFinal(Span<byte> output)
+        {
+            if (bufOff % buf.Length != 0)
+                throw new DataLengthException("Input must be a multiple of blocksize");
+
+            Check.OutputLength(output, macSize, "output buffer too short");
+
+            //Last block
+            Xor(c, 0, buf, 0, cTemp);
+            Xor(cTemp, 0, kDelta, 0, c);
+            engine.ProcessBlock(c, c);
+
+            c.AsSpan(0, macSize).CopyTo(output);
+
+            return macSize;
+        }
+#endif
+
+        public void Reset()
+        {
             Arrays.Fill(c, (byte)0x00);
             Arrays.Fill(cTemp, (byte)0x00);
             Arrays.Fill(kDelta, (byte)0x00);
@@ -156,5 +218,5 @@ namespace Org.BouncyCastle.Crypto.Macs
             engine.ProcessBlock(kDelta, 0, kDelta, 0);
             bufOff = 0;
         }
-     }
+    }
 }
diff --git a/crypto/src/crypto/macs/GMac.cs b/crypto/src/crypto/macs/GMac.cs
index 0554c44f0..aa124bb04 100644
--- a/crypto/src/crypto/macs/GMac.cs
+++ b/crypto/src/crypto/macs/GMac.cs
@@ -52,10 +52,8 @@ namespace Org.BouncyCastle.Crypto.Macs
         /// </summary>
         public void Init(ICipherParameters parameters)
         {
-            if (parameters is ParametersWithIV)
+            if (parameters is ParametersWithIV param)
             {
-                ParametersWithIV param = (ParametersWithIV)parameters;
-
                 byte[] iv = param.GetIV();
                 KeyParameter keyParam = (KeyParameter)param.Parameters;
 
@@ -88,6 +86,13 @@ namespace Org.BouncyCastle.Crypto.Macs
             cipher.ProcessAadBytes(input, inOff, len);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            cipher.ProcessAadBytes(input);
+        }
+#endif
+
         public int DoFinal(byte[] output, int outOff)
         {
             try
@@ -101,6 +106,17 @@ namespace Org.BouncyCastle.Crypto.Macs
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int DoFinal(Span<byte> output)
+        {
+            // TODO[span] call cipher.DoFinal(Span<byte) when available
+            byte[] tmp = new byte[GetMacSize()];
+            int result = DoFinal(tmp, 0);
+            tmp.CopyTo(output);
+            return result;
+        }
+#endif
+
         public void Reset()
         {
             cipher.Reset();
diff --git a/crypto/src/crypto/macs/GOST28147Mac.cs b/crypto/src/crypto/macs/GOST28147Mac.cs
index 33c2d67ee..8c39fc6b0 100644
--- a/crypto/src/crypto/macs/GOST28147Mac.cs
+++ b/crypto/src/crypto/macs/GOST28147Mac.cs
@@ -1,7 +1,7 @@
 using System;
 
-using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Macs
@@ -9,10 +9,11 @@ namespace Org.BouncyCastle.Crypto.Macs
 	/**
 	* implementation of GOST 28147-89 MAC
 	*/
-	public class Gost28147Mac : IMac
+	public class Gost28147Mac
+		: IMac
 	{
-		private const int			blockSize = 8;
-		private const int			macSize = 4;
+		private const int			BlockSize = 8;
+		private const int			MacSize = 4;
 		private int					bufOff;
 		private byte[]				buf;
 		private byte[]				mac;
@@ -36,8 +37,8 @@ namespace Org.BouncyCastle.Crypto.Macs
 
 		public Gost28147Mac()
 		{
-			mac = new byte[blockSize];
-			buf = new byte[blockSize];
+			mac = new byte[BlockSize];
+			buf = new byte[BlockSize];
 			bufOff = 0;
 		}
 
@@ -50,22 +51,19 @@ namespace Org.BouncyCastle.Crypto.Macs
 			int[] key = new int[8];
 			for(int i=0; i!=8; i++)
 			{
-				key[i] = bytesToint(userKey,i*4);
+				key[i] = (int)Pack.LE_To_UInt32(userKey, i * 4);
 			}
 
 			return key;
 		}
 
-		public void Init(
-			ICipherParameters parameters)
+		public void Init(ICipherParameters parameters)
 		{
 			Reset();
-			buf = new byte[blockSize];
+			buf = new byte[BlockSize];
             macIV = null;
-            if (parameters is ParametersWithSBox)
+            if (parameters is ParametersWithSBox param)
 			{
-				ParametersWithSBox param = (ParametersWithSBox)parameters;
-
 				//
 				// Set the S-Box
 				//
@@ -79,17 +77,15 @@ namespace Org.BouncyCastle.Crypto.Macs
 					workingKey = GenerateWorkingKey(((KeyParameter)param.Parameters).GetKey());
 				}
 			}
-			else if (parameters is KeyParameter)
+			else if (parameters is KeyParameter keyParameter)
 			{
-				workingKey = GenerateWorkingKey(((KeyParameter)parameters).GetKey());
+				workingKey = GenerateWorkingKey(keyParameter.GetKey());
 			}
-            else if (parameters is ParametersWithIV)
+            else if (parameters is ParametersWithIV ivParam)
             {
-                ParametersWithIV p = (ParametersWithIV)parameters;
-
-                workingKey = GenerateWorkingKey(((KeyParameter)p.Parameters).GetKey());
-                Array.Copy(p.GetIV(), 0, mac, 0, mac.Length);
-                macIV = p.GetIV(); // don't skip the initial CM5Func
+                workingKey = GenerateWorkingKey(((KeyParameter)ivParam.Parameters).GetKey());
+				macIV = ivParam.GetIV(); // don't skip the initial CM5Func
+				Array.Copy(macIV, 0, mac, 0, mac.Length);
             }
 			else
 			{
@@ -105,10 +101,10 @@ namespace Org.BouncyCastle.Crypto.Macs
 
 		public int GetMacSize()
 		{
-			return macSize;
+			return MacSize;
 		}
 
-		private int gost28147_mainStep(int n1, int key)
+		private int Gost28147_mainStep(int n1, int key)
 		{
 			int cm = (key + n1); // CM1
 
@@ -130,178 +126,222 @@ namespace Org.BouncyCastle.Crypto.Macs
 			return omLeft | omRight;
 		}
 
-		private void gost28147MacFunc(
+		private void Gost28147MacFunc(
 			int[]	workingKey,
 			byte[]	input,
 			int		inOff,
 			byte[]	output,
 			int		outOff)
 		{
-			int N1, N2, tmp;  //tmp -> for saving N1
-			N1 = bytesToint(input, inOff);
-			N2 = bytesToint(input, inOff + 4);
+			int N1 = (int)Pack.LE_To_UInt32(input, inOff);
+			int N2 = (int)Pack.LE_To_UInt32(input, inOff + 4);
+			int tmp;  //tmp -> for saving N1
 
 			for (int k = 0; k < 2; k++)  // 1-16 steps
 			{
 				for (int j = 0; j < 8; j++)
 				{
 					tmp = N1;
-					N1 = N2 ^ gost28147_mainStep(N1, workingKey[j]); // CM2
+					N1 = N2 ^ Gost28147_mainStep(N1, workingKey[j]); // CM2
 					N2 = tmp;
 				}
 			}
 
-			intTobytes(N1, output, outOff);
-			intTobytes(N2, output, outOff + 4);
-		}
-
-		//array of bytes to type int
-		private static int bytesToint(
-			byte[]	input,
-			int		inOff)
-		{
-			return (int)((input[inOff + 3] << 24) & 0xff000000) + ((input[inOff + 2] << 16) & 0xff0000)
-				+ ((input[inOff + 1] << 8) & 0xff00) + (input[inOff] & 0xff);
-		}
-
-		//int to array of bytes
-		private static void intTobytes(
-			int		num,
-			byte[]	output,
-			int		outOff)
-		{
-			output[outOff + 3] = (byte)(num >> 24);
-			output[outOff + 2] = (byte)(num >> 16);
-			output[outOff + 1] = (byte)(num >> 8);
-			output[outOff] =     (byte)num;
-		}
-
-		private static byte[] CM5func(
-			byte[]	buf,
-			int		bufOff,
-			byte[]	mac)
-		{
-			byte[] sum = new byte[buf.Length - bufOff];
-
-			Array.Copy(buf, bufOff, sum, 0, mac.Length);
-
-			for (int i = 0; i != mac.Length; i++)
-			{
-				sum[i] = (byte)(sum[i] ^ mac[i]);
-			}
-
-			return sum;
+			Pack.UInt32_To_LE((uint)N1, output, outOff);
+			Pack.UInt32_To_LE((uint)N2, output, outOff + 4);
 		}
 
-		public void Update(
-			byte input)
+		public void Update(byte input)
 		{
 			if (bufOff == buf.Length)
 			{
-				byte[] sumbuf = new byte[buf.Length];
-				Array.Copy(buf, 0, sumbuf, 0, mac.Length);
-
+				byte[] sum = new byte[buf.Length];
 				if (firstStep)
 				{
 					firstStep = false;
                     if (macIV != null)
                     {
-                        sumbuf = CM5func(buf, 0, macIV);
+                        Cm5Func(buf, 0, macIV, sum);
                     }
-                }
+					else
+                    {
+						Array.Copy(buf, 0, sum, 0, mac.Length);
+					}
+				}
 				else
 				{
-					sumbuf = CM5func(buf, 0, mac);
+					Cm5Func(buf, 0, mac, sum);
 				}
 
-				gost28147MacFunc(workingKey, sumbuf, 0, mac, 0);
+				Gost28147MacFunc(workingKey, sum, 0, mac, 0);
 				bufOff = 0;
 			}
 
 			buf[bufOff++] = input;
 		}
 
-		public void BlockUpdate(
-			byte[]	input,
-			int		inOff,
-			int		len)
+		public void BlockUpdate(byte[] input, int inOff, int len)
 		{
 			if (len < 0)
 				throw new ArgumentException("Can't have a negative input length!");
 
-			int gapLen = blockSize - bufOff;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			BlockUpdate(input.AsSpan(inOff, len));
+#else
+			int gapLen = BlockSize - bufOff;
 
 			if (len > gapLen)
 			{
 				Array.Copy(input, inOff, buf, bufOff, gapLen);
 
-				byte[] sumbuf = new byte[buf.Length];
-				Array.Copy(buf, 0, sumbuf, 0, mac.Length);
-
+				byte[] sum = new byte[buf.Length];
 				if (firstStep)
 				{
 					firstStep = false;
                     if (macIV != null)
                     {
-                        sumbuf = CM5func(buf, 0, macIV);
+                        Cm5Func(buf, 0, macIV, sum);
                     }
-                }
+					else
+                    {
+						Array.Copy(buf, 0, sum, 0, mac.Length);
+					}
+				}
 				else
 				{
-					sumbuf = CM5func(buf, 0, mac);
+					Cm5Func(buf, 0, mac, sum);
 				}
 
-				gost28147MacFunc(workingKey, sumbuf, 0, mac, 0);
+				Gost28147MacFunc(workingKey, sum, 0, mac, 0);
 
 				bufOff = 0;
 				len -= gapLen;
 				inOff += gapLen;
 
-				while (len > blockSize)
+				while (len > BlockSize)
 				{
-					sumbuf = CM5func(input, inOff, mac);
-					gost28147MacFunc(workingKey, sumbuf, 0, mac, 0);
+					Cm5Func(input, inOff, mac, sum);
+					Gost28147MacFunc(workingKey, sum, 0, mac, 0);
 
-					len -= blockSize;
-					inOff += blockSize;
+					len -= BlockSize;
+					inOff += BlockSize;
 				}
 			}
 
 			Array.Copy(input, inOff, buf, bufOff, len);
 
 			bufOff += len;
+#endif
 		}
 
-		public int DoFinal(
-			byte[]	output,
-			int		outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public void BlockUpdate(ReadOnlySpan<byte> input)
 		{
+			int gapLen = BlockSize - bufOff;
+
+			if (input.Length > gapLen)
+			{
+				input[..gapLen].CopyTo(buf.AsSpan(bufOff));
+
+				byte[] sum = new byte[buf.Length];
+				if (firstStep)
+				{
+					firstStep = false;
+                    if (macIV != null)
+                    {
+                        Cm5Func(buf, macIV, sum);
+                    }
+                    else
+                    {
+						Array.Copy(buf, 0, sum, 0, mac.Length);
+					}
+				}
+				else
+				{
+					Cm5Func(buf, mac, sum);
+				}
+
+				Gost28147MacFunc(workingKey, sum, 0, mac, 0);
+
+				bufOff = 0;
+				input = input[gapLen..];
+
+				while (input.Length > BlockSize)
+				{
+					Cm5Func(input, mac, sum);
+					Gost28147MacFunc(workingKey, sum, 0, mac, 0);
+
+					input = input[BlockSize..];
+				}
+			}
+
+			input.CopyTo(buf.AsSpan(bufOff));
+
+			bufOff += input.Length;
+		}
+#endif
+
+		public int DoFinal(byte[] output, int outOff)
+		{
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			return DoFinal(output.AsSpan(outOff));
+#else
 			//padding with zero
-			while (bufOff < blockSize)
+			while (bufOff < BlockSize)
 			{
 				buf[bufOff++] = 0;
 			}
 
-			byte[] sumbuf = new byte[buf.Length];
-			Array.Copy(buf, 0, sumbuf, 0, mac.Length);
+			byte[] sum = new byte[buf.Length];
+			if (firstStep)
+			{
+				firstStep = false;
+				Array.Copy(buf, 0, sum, 0, mac.Length);
+			}
+			else
+			{
+				Cm5Func(buf, 0, mac, sum);
+			}
+
+			Gost28147MacFunc(workingKey, sum, 0, mac, 0);
 
+			Array.Copy(mac, (mac.Length/2)-MacSize, output, outOff, MacSize);
+
+			Reset();
+
+			return MacSize;
+#endif
+		}
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public int DoFinal(Span<byte> output)
+		{
+			//padding with zero
+			while (bufOff < BlockSize)
+			{
+				buf[bufOff++] = 0;
+			}
+
+			byte[] sum = new byte[buf.Length];
 			if (firstStep)
 			{
 				firstStep = false;
+				Array.Copy(buf, 0, sum, 0, mac.Length);
 			}
 			else
 			{
-				sumbuf = CM5func(buf, 0, mac);
+				Cm5Func(buf, 0, mac, sum);
 			}
 
-			gost28147MacFunc(workingKey, sumbuf, 0, mac, 0);
+			Gost28147MacFunc(workingKey, sum, 0, mac, 0);
 
-			Array.Copy(mac, (mac.Length/2)-macSize, output, outOff, macSize);
+			mac.AsSpan((mac.Length / 2) - MacSize, MacSize).CopyTo(output);
 
 			Reset();
 
-			return macSize;
+			return MacSize;
 		}
+#endif
 
 		public void Reset()
 		{
@@ -311,5 +351,23 @@ namespace Org.BouncyCastle.Crypto.Macs
 
 			firstStep = true;
 		}
+
+		private static void Cm5Func(byte[] buf, int bufOff, byte[] mac, byte[] sum)
+		{
+			for (int i = 0; i < BlockSize; ++i)
+			{
+				sum[i] = (byte)(buf[bufOff + i] ^ mac[i]);
+			}
+		}
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		private static void Cm5Func(ReadOnlySpan<byte> buffer, ReadOnlySpan<byte> mac, Span<byte> sum)
+		{
+			for (int i = 0; i < BlockSize; ++i)
+			{
+				sum[i] = (byte)(buffer[i] ^ mac[i]);
+			}
+		}
+#endif
 	}
 }
diff --git a/crypto/src/crypto/macs/HMac.cs b/crypto/src/crypto/macs/HMac.cs
index a717ce4f7..3445a945b 100644
--- a/crypto/src/crypto/macs/HMac.cs
+++ b/crypto/src/crypto/macs/HMac.cs
@@ -99,14 +99,24 @@ namespace Org.BouncyCastle.Crypto.Macs
             digest.BlockUpdate(input, inOff, len);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            digest.BlockUpdate(input);
+        }
+#endif
+
         public virtual int DoFinal(byte[] output, int outOff)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return DoFinal(output.AsSpan(outOff));
+#else
             digest.DoFinal(outputBuf, blockLength);
 
 			if (opadState != null)
 			{
 				((IMemoable)digest).Reset(opadState);
-				digest.BlockUpdate(outputBuf, blockLength, digest.GetDigestSize());
+				digest.BlockUpdate(outputBuf, blockLength, digestSize);
 			}
 			else
 			{
@@ -127,7 +137,40 @@ namespace Org.BouncyCastle.Crypto.Macs
 			}
 
             return len;
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int DoFinal(Span<byte> output)
+        {
+            digest.DoFinal(outputBuf.AsSpan(blockLength));
+
+            if (opadState != null)
+            {
+                ((IMemoable)digest).Reset(opadState);
+                digest.BlockUpdate(outputBuf.AsSpan(blockLength, digestSize));
+            }
+            else
+            {
+                digest.BlockUpdate(outputBuf);
+            }
+
+            int len = digest.DoFinal(output);
+
+            Array.Clear(outputBuf, blockLength, digestSize);
+
+            if (ipadState != null)
+            {
+                ((IMemoable)digest).Reset(ipadState);
+            }
+            else
+            {
+                digest.BlockUpdate(inputPad);
+            }
+
+            return len;
         }
+#endif
 
         /**
         * Reset the mac generator.
diff --git a/crypto/src/crypto/macs/ISO9797Alg3Mac.cs b/crypto/src/crypto/macs/ISO9797Alg3Mac.cs
index 6fee619c1..40a68007e 100644
--- a/crypto/src/crypto/macs/ISO9797Alg3Mac.cs
+++ b/crypto/src/crypto/macs/ISO9797Alg3Mac.cs
@@ -180,14 +180,14 @@ namespace Org.BouncyCastle.Crypto.Macs
 			buf[bufOff++] = input;
 		}
 
-		public void BlockUpdate(
-			byte[]	input,
-			int		inOff,
-			int		len)
+		public void BlockUpdate(byte[] input, int inOff, int len)
 		{
 			if (len < 0)
 				throw new ArgumentException("Can't have a negative input length!");
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			BlockUpdate(input.AsSpan(inOff, len));
+#else
 			int blockSize = cipher.GetBlockSize();
 			int resultLen = 0;
 			int gapLen = blockSize - bufOff;
@@ -214,13 +214,44 @@ namespace Org.BouncyCastle.Crypto.Macs
 			Array.Copy(input, inOff, buf, bufOff, len);
 
 			bufOff += len;
+#endif
 		}
 
-		public int DoFinal(
-			byte[]	output,
-			int		outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public void BlockUpdate(ReadOnlySpan<byte> input)
 		{
 			int blockSize = cipher.GetBlockSize();
+			int resultLen = 0;
+			int gapLen = blockSize - bufOff;
+
+			if (input.Length > gapLen)
+			{
+				input[..gapLen].CopyTo(buf.AsSpan(bufOff));
+
+				resultLen += cipher.ProcessBlock(buf, mac);
+
+				bufOff = 0;
+				input = input[gapLen..];
+
+				while (input.Length > blockSize)
+				{
+					resultLen += cipher.ProcessBlock(input, mac);
+					input = input[blockSize..];
+				}
+			}
+
+			input.CopyTo(buf.AsSpan(bufOff));
+
+			bufOff += input.Length;
+		}
+#endif
+
+		public int DoFinal(byte[] output, int outOff)
+		{
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			return DoFinal(output.AsSpan(outOff));
+#else
+			int blockSize = cipher.GetBlockSize();
 
 			if (padding == null)
 			{
@@ -258,7 +289,52 @@ namespace Org.BouncyCastle.Crypto.Macs
 			Reset();
 
 			return macSize;
+#endif
+		}
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public int DoFinal(Span<byte> output)
+		{
+			int blockSize = cipher.GetBlockSize();
+
+			if (padding == null)
+			{
+				// pad with zeroes
+				while (bufOff < blockSize)
+				{
+					buf[bufOff++] = 0;
+				}
+			}
+			else
+			{
+				if (bufOff == blockSize)
+				{
+					cipher.ProcessBlock(buf, mac);
+					bufOff = 0;
+				}
+
+				padding.AddPadding(buf, bufOff);
+			}
+
+			cipher.ProcessBlock(buf, mac);
+
+			// Added to code from base class
+			DesEngine deseng = new DesEngine();
+
+			deseng.Init(false, this.lastKey2);
+			deseng.ProcessBlock(mac, mac);
+
+			deseng.Init(true, this.lastKey3);
+			deseng.ProcessBlock(mac, mac);
+			// ****
+
+			mac.AsSpan(0, macSize).CopyTo(output);
+
+			Reset();
+
+			return macSize;
 		}
+#endif
 
 		/**
 		* Reset the mac generator.
diff --git a/crypto/src/crypto/macs/KMac.cs b/crypto/src/crypto/macs/KMac.cs
index 05031ac2f..ce6c9f701 100644
--- a/crypto/src/crypto/macs/KMac.cs
+++ b/crypto/src/crypto/macs/KMac.cs
@@ -39,6 +39,16 @@ namespace Org.BouncyCastle.Crypto.Macs
             cshake.BlockUpdate(input, inOff, len);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            if (!initialised)
+                throw new InvalidOperationException("KMAC not initialized");
+
+            cshake.BlockUpdate(input);
+        }
+#endif
+
         public int DoFinal(byte[] output, int outOff)
         {
             if (firstOutput)
@@ -58,6 +68,27 @@ namespace Org.BouncyCastle.Crypto.Macs
             return rv;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int DoFinal(Span<byte> output)
+        {
+            if (firstOutput)
+            {
+                if (!initialised)
+                    throw new InvalidOperationException("KMAC not initialized");
+
+                Span<byte> lengthEncoding = stackalloc byte[9];
+                int count = XofUtilities.RightEncode(GetMacSize() * 8, lengthEncoding);
+                cshake.BlockUpdate(lengthEncoding[..count]);
+            }
+
+            int rv = cshake.OutputFinal(output[..GetMacSize()]);
+
+            Reset();
+
+            return rv;
+        }
+#endif
+
         public int DoFinal(byte[] output, int outOff, int outLen)
         {
             if (firstOutput)
@@ -77,6 +108,27 @@ namespace Org.BouncyCastle.Crypto.Macs
             return rv;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int OutputFinal(Span<byte> output)
+        {
+            if (firstOutput)
+            {
+                if (!initialised)
+                    throw new InvalidOperationException("KMAC not initialized");
+
+                Span<byte> lengthEncoding = stackalloc byte[9];
+                int count = XofUtilities.RightEncode(output.Length * 8, lengthEncoding);
+                cshake.BlockUpdate(lengthEncoding[..count]);
+            }
+
+            int rv = cshake.OutputFinal(output);
+
+            Reset();
+
+            return rv;
+        }
+#endif
+
         public int DoOutput(byte[] output, int outOff, int outLen)
         {
             if (firstOutput)
@@ -94,6 +146,25 @@ namespace Org.BouncyCastle.Crypto.Macs
             return cshake.DoOutput(output, outOff, outLen);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int Output(Span<byte> output)
+        {
+            if (firstOutput)
+            {
+                if (!initialised)
+                    throw new InvalidOperationException("KMAC not initialized");
+
+                Span<byte> lengthEncoding = stackalloc byte[9];
+                int count = XofUtilities.RightEncode(0, lengthEncoding);
+                cshake.BlockUpdate(lengthEncoding[..count]);
+
+                firstOutput = false;
+            }
+
+            return cshake.Output(output);
+        }
+#endif
+
         public int GetByteLength()
         {
             return cshake.GetByteLength();
diff --git a/crypto/src/crypto/macs/Poly1305.cs b/crypto/src/crypto/macs/Poly1305.cs
index 595d9b051..eb90e387e 100644
--- a/crypto/src/crypto/macs/Poly1305.cs
+++ b/crypto/src/crypto/macs/Poly1305.cs
@@ -167,15 +167,22 @@ namespace Org.BouncyCastle.Crypto.Macs
             currentBlock[currentBlockOffset++] = input;
             if (currentBlockOffset == BlockSize)
             {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                ProcessBlock(currentBlock);
+#else
                 ProcessBlock(currentBlock, 0);
+#endif
                 currentBlockOffset = 0;
             }
         }
 
         public void BlockUpdate(byte[] input, int inOff, int len)
         {
-            // TODO Validity check on arguments
+            Check.DataLength(input, inOff, len, "input buffer too short");
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            BlockUpdate(input.AsSpan(inOff, len));
+#else
             int available = BlockSize - currentBlockOffset;
             if (len < available)
             {
@@ -189,37 +196,108 @@ namespace Org.BouncyCastle.Crypto.Macs
             {
                 Array.Copy(input, inOff, currentBlock, currentBlockOffset, available);
                 pos = available;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                ProcessBlock(currentBlock);
+#else
                 ProcessBlock(currentBlock, 0);
+#endif
             }
 
             int remaining;
             while ((remaining = len - pos) >= BlockSize)
             {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                ProcessBlock(input.AsSpan(inOff + pos));
+#else
                 ProcessBlock(input, inOff + pos);
+#endif
                 pos += BlockSize;
             }
 
             Array.Copy(input, inOff + pos, currentBlock, 0, remaining);
             currentBlockOffset = remaining;
+#endif
         }
 
-        private void ProcessBlock(byte[] buf, int off)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            int available = BlockSize - currentBlockOffset;
+            if (input.Length < available)
+            {
+                input.CopyTo(currentBlock.AsSpan(currentBlockOffset));
+                currentBlockOffset += input.Length;
+                return;
+            }
+
+            int pos = 0;
+            if (currentBlockOffset > 0)
+            {
+                input[..available].CopyTo(currentBlock.AsSpan(currentBlockOffset));
+                pos = available;
+                ProcessBlock(currentBlock);
+            }
+
+            int remaining;
+            while ((remaining = input.Length - pos) >= BlockSize)
+            {
+                ProcessBlock(input[pos..]);
+                pos += BlockSize;
+            }
+
+            input[pos..].CopyTo(currentBlock);
+            currentBlockOffset = remaining;
+        }
+#endif
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void ProcessBlock(ReadOnlySpan<byte> block)
         {
 #if NETCOREAPP3_0_OR_GREATER
             if (BitConverter.IsLittleEndian)
             {
                 Span<uint> t = stackalloc uint[4];
-                Unsafe.CopyBlockUnaligned(ref Unsafe.As<uint, byte>(ref t[0]), ref buf[off], 16);
+                Unsafe.CopyBlockUnaligned(ref Unsafe.As<uint, byte>(ref t[0]), ref Unsafe.AsRef(block[0]), 16);
 
                 h0 +=   t[0]                        & 0x3ffffffU;
                 h1 += ((t[1] <<  6) | (t[0] >> 26)) & 0x3ffffffU;
                 h2 += ((t[2] << 12) | (t[1] >> 20)) & 0x3ffffffU;
                 h3 += ((t[3] << 18) | (t[2] >> 14)) & 0x3ffffffU;
-                h4 +=     (1 << 24) | (t[3] >>  8);
+                h4 += (1 << 24) | (t[3] >> 8);
             }
             else
 #endif
             {
+                uint t0 = Pack.LE_To_UInt32(block);
+                uint t1 = Pack.LE_To_UInt32(block[4..]);
+                uint t2 = Pack.LE_To_UInt32(block[8..]);
+                uint t3 = Pack.LE_To_UInt32(block[12..]);
+
+                h0 +=   t0                      & 0x3ffffffU;
+                h1 += ((t1 <<  6) | (t0 >> 26)) & 0x3ffffffU;
+                h2 += ((t2 << 12) | (t1 >> 20)) & 0x3ffffffU;
+                h3 += ((t3 << 18) | (t2 >> 14)) & 0x3ffffffU;
+                h4 +=  ( 1 << 24) | (t3 >>  8);
+            }
+
+            ulong tp0 = (ulong)h0 * r0 + (ulong)h1 * s4 + (ulong)h2 * s3 + (ulong)h3 * s2 + (ulong)h4 * s1;
+            ulong tp1 = (ulong)h0 * r1 + (ulong)h1 * r0 + (ulong)h2 * s4 + (ulong)h3 * s3 + (ulong)h4 * s2;
+            ulong tp2 = (ulong)h0 * r2 + (ulong)h1 * r1 + (ulong)h2 * r0 + (ulong)h3 * s4 + (ulong)h4 * s3;
+            ulong tp3 = (ulong)h0 * r3 + (ulong)h1 * r2 + (ulong)h2 * r1 + (ulong)h3 * r0 + (ulong)h4 * s4;
+            ulong tp4 = (ulong)h0 * r4 + (ulong)h1 * r3 + (ulong)h2 * r2 + (ulong)h3 * r1 + (ulong)h4 * r0;
+
+            h0 = (uint)tp0 & 0x3ffffff; tp1 += (tp0 >> 26);
+            h1 = (uint)tp1 & 0x3ffffff; tp2 += (tp1 >> 26);
+            h2 = (uint)tp2 & 0x3ffffff; tp3 += (tp2 >> 26);
+            h3 = (uint)tp3 & 0x3ffffff; tp4 += (tp3 >> 26);
+            h4 = (uint)tp4 & 0x3ffffff;
+            h0 += (uint)(tp4 >> 26) * 5;
+            h1 += h0 >> 26; h0 &= 0x3ffffff;
+        }
+#else
+        private void ProcessBlock(byte[] buf, int off)
+        {
+            {
                 uint t0 = Pack.LE_To_UInt32(buf, off +  0);
                 uint t1 = Pack.LE_To_UInt32(buf, off +  4);
                 uint t2 = Pack.LE_To_UInt32(buf, off +  8);
@@ -246,10 +324,14 @@ namespace Org.BouncyCastle.Crypto.Macs
             h0 += (uint)(tp4 >> 26) * 5;
             h1 += h0 >> 26; h0 &= 0x3ffffff;
         }
+#endif
 
         public int DoFinal(byte[] output, int outOff)
         {
-            Check.DataLength(output, outOff, BlockSize, "Output buffer is too short.");
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return DoFinal(output.AsSpan(outOff));
+#else
+            Check.OutputLength(output, outOff, BlockSize, "output buffer is too short.");
 
             if (currentBlockOffset > 0)
             {
@@ -289,7 +371,54 @@ namespace Org.BouncyCastle.Crypto.Macs
 
             Reset();
             return BlockSize;
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int DoFinal(Span<byte> output)
+        {
+            Check.OutputLength(output, BlockSize, "output buffer is too short.");
+
+            if (currentBlockOffset > 0)
+            {
+                // Process padded block
+                if (currentBlockOffset < BlockSize)
+                {
+                    currentBlock[currentBlockOffset++] = 1;
+                    while (currentBlockOffset < BlockSize)
+                    {
+                        currentBlock[currentBlockOffset++] = 0;
+                    }
+
+                    h4 -= (1 << 24);
+                }
+
+                ProcessBlock(currentBlock);
+            }
+
+            Debug.Assert(h4 >> 26 == 0);
+
+            //h0 += (h4 >> 26) * 5U + 5U; h4 &= 0x3ffffff;
+            h0 += 5U;
+            h1 += h0 >> 26; h0 &= 0x3ffffff;
+            h2 += h1 >> 26; h1 &= 0x3ffffff;
+            h3 += h2 >> 26; h2 &= 0x3ffffff;
+            h4 += h3 >> 26; h3 &= 0x3ffffff;
+
+            long c = ((int)(h4 >> 26) - 1) * 5;
+            c += (long)k0 + ((h0) | (h1 << 26));
+            Pack.UInt32_To_LE((uint)c, output); c >>= 32;
+            c += (long)k1 + ((h1 >> 6) | (h2 << 20));
+            Pack.UInt32_To_LE((uint)c, output[4..]); c >>= 32;
+            c += (long)k2 + ((h2 >> 12) | (h3 << 14));
+            Pack.UInt32_To_LE((uint)c, output[8..]); c >>= 32;
+            c += (long)k3 + ((h3 >> 18) | (h4 << 8));
+            Pack.UInt32_To_LE((uint)c, output[12..]);
+
+            Reset();
+            return BlockSize;
         }
+#endif
 
         public void Reset()
         {
diff --git a/crypto/src/crypto/macs/SipHash.cs b/crypto/src/crypto/macs/SipHash.cs
index e1a19fa5b..2e8a89e3d 100644
--- a/crypto/src/crypto/macs/SipHash.cs
+++ b/crypto/src/crypto/macs/SipHash.cs
@@ -80,6 +80,9 @@ namespace Org.BouncyCastle.Crypto.Macs
 
         public virtual void BlockUpdate(byte[] input, int offset, int length)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            BlockUpdate(input.AsSpan(offset, length));
+#else
             int i = 0, fullWords = length & ~7;
             if (wordPos == 0)
             {
@@ -115,8 +118,51 @@ namespace Org.BouncyCastle.Crypto.Macs
                     }
                 }
             }
+#endif
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            int length = input.Length;
+            int i = 0, fullWords = length & ~7;
+            if (wordPos == 0)
+            {
+                for (; i < fullWords; i += 8)
+                {
+                    m = (long)Pack.LE_To_UInt64(input[i..]);
+                    ProcessMessageWord();
+                }
+                for (; i < length; ++i)
+                {
+                    m = (long)(((ulong)m >> 8) | ((ulong)input[i] << 56));
+                }
+                wordPos = length - fullWords;
+            }
+            else
+            {
+                int bits = wordPos << 3;
+                for (; i < fullWords; i += 8)
+                {
+                    ulong n = Pack.LE_To_UInt64(input[i..]);
+                    m = (long)((n << bits) | ((ulong)m >> -bits));
+                    ProcessMessageWord();
+                    m = (long)n;
+                }
+                for (; i < length; ++i)
+                {
+                    m = (long)(((ulong)m >> 8) | ((ulong)input[i] << 56));
+
+                    if (++wordPos == 8)
+                    {
+                        ProcessMessageWord();
+                        wordPos = 0;
+                    }
+                }
+            }
+        }
+#endif
+
         public virtual long DoFinal()
         {
             // NOTE: 2 distinct shifts to avoid "64-bit shift" when wordPos == 0
@@ -144,6 +190,15 @@ namespace Org.BouncyCastle.Crypto.Macs
             return 8;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int DoFinal(Span<byte> output)
+        {
+            long result = DoFinal();
+            Pack.UInt64_To_LE((ulong)result, output);
+            return 8;
+        }
+#endif
+
         public virtual void Reset()
         {
             v0 = k0 ^ 0x736f6d6570736575L;
diff --git a/crypto/src/crypto/macs/SkeinMac.cs b/crypto/src/crypto/macs/SkeinMac.cs
index 07eff24f4..aaf5b312d 100644
--- a/crypto/src/crypto/macs/SkeinMac.cs
+++ b/crypto/src/crypto/macs/SkeinMac.cs
@@ -106,13 +106,26 @@ namespace Org.BouncyCastle.Crypto.Macs
 
 		public void BlockUpdate(byte[] input, int inOff, int len)
 		{
-			engine.Update(input, inOff, len);
+			engine.BlockUpdate(input, inOff, len);
 		}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public void BlockUpdate(ReadOnlySpan<byte> input)
+		{
+			engine.BlockUpdate(input);
+		}
+#endif
+
 		public int DoFinal(byte[] output, int outOff)
 		{
 			return engine.DoFinal(output, outOff);
 		}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public int DoFinal(Span<byte> output)
+		{
+			return engine.DoFinal(output);
+		}
+#endif
 	}
 }
diff --git a/crypto/src/crypto/macs/VMPCMac.cs b/crypto/src/crypto/macs/VMPCMac.cs
index 6f2da075c..c2902179f 100644
--- a/crypto/src/crypto/macs/VMPCMac.cs
+++ b/crypto/src/crypto/macs/VMPCMac.cs
@@ -22,6 +22,9 @@ namespace Org.BouncyCastle.Crypto.Macs
 
 		public virtual int DoFinal(byte[] output, int outOff)
 		{
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			return DoFinal(output.AsSpan(outOff));
+#else
 			// Execute the Post-Processing Phase
 			for (int r = 1; r < 25; r++)
 			{
@@ -68,8 +71,61 @@ namespace Org.BouncyCastle.Crypto.Macs
 			Reset();
 
 			return M.Length;
+#endif
 		}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public virtual int DoFinal(Span<byte> output)
+        {
+			// Execute the Post-Processing Phase
+			for (int r = 1; r < 25; r++)
+			{
+				s = P[(s + P[n & 0xff]) & 0xff];
+
+				x4 = P[(x4 + x3 + r) & 0xff];
+				x3 = P[(x3 + x2 + r) & 0xff];
+				x2 = P[(x2 + x1 + r) & 0xff];
+				x1 = P[(x1 + s + r) & 0xff];
+				T[g & 0x1f] = (byte)(T[g & 0x1f] ^ x1);
+				T[(g + 1) & 0x1f] = (byte)(T[(g + 1) & 0x1f] ^ x2);
+				T[(g + 2) & 0x1f] = (byte)(T[(g + 2) & 0x1f] ^ x3);
+				T[(g + 3) & 0x1f] = (byte)(T[(g + 3) & 0x1f] ^ x4);
+				g = (byte)((g + 4) & 0x1f);
+
+				byte temp = P[n & 0xff];
+				P[n & 0xff] = P[s & 0xff];
+				P[s & 0xff] = temp;
+				n = (byte)((n + 1) & 0xff);
+			}
+
+			// Input T to the IV-phase of the VMPC KSA
+			for (int m = 0; m < 768; m++)
+			{
+				s = P[(s + P[m & 0xff] + T[m & 0x1f]) & 0xff];
+				byte temp = P[m & 0xff];
+				P[m & 0xff] = P[s & 0xff];
+				P[s & 0xff] = temp;
+			}
+
+			// Store 20 new outputs of the VMPC Stream Cipher input table M
+			byte[] M = new byte[20];
+			for (int i = 0; i < 20; i++)
+			{
+				s = P[(s + P[i & 0xff]) & 0xff];
+				M[i] = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff];
+
+				byte temp = P[i & 0xff];
+				P[i & 0xff] = P[s & 0xff];
+				P[s & 0xff] = temp;
+			}
+
+			M.CopyTo(output);
+			Reset();
+
+			return M.Length;
+		}
+#endif
+
 		public virtual string AlgorithmName
 		{
 			get { return "VMPC-MAC"; }
@@ -159,15 +215,24 @@ namespace Org.BouncyCastle.Crypto.Macs
 			n = (byte) ((n + 1) & 0xff);
 		}
 
-		public virtual void BlockUpdate(byte[] input, int inOff, int len)
+		public virtual void BlockUpdate(byte[] input, int inOff, int inLen)
 		{
-			if ((inOff + len) > input.Length)
-				throw new DataLengthException("input buffer too short");
+			Check.DataLength(input, inOff, inLen, "input buffer too short");
 
-			for (int i = 0; i < len; i++)
+			for (int i = 0; i < inLen; i++)
 			{
 				Update(input[inOff + i]);
 			}
 		}
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+			for (int i = 0; i < input.Length; i++)
+			{
+				Update(input[i]);
+			}
+		}
+#endif
 	}
 }
diff --git a/crypto/src/crypto/modes/CbcBlockCipher.cs b/crypto/src/crypto/modes/CbcBlockCipher.cs
index 9345fd8c2..eb89c81ee 100644
--- a/crypto/src/crypto/modes/CbcBlockCipher.cs
+++ b/crypto/src/crypto/modes/CbcBlockCipher.cs
@@ -59,15 +59,12 @@ namespace Org.BouncyCastle.Crypto.Modes
 
             this.encrypting = forEncryption;
 
-            if (parameters is ParametersWithIV)
+            if (parameters is ParametersWithIV ivParam)
             {
-                ParametersWithIV ivParam = (ParametersWithIV)parameters;
-                byte[]      iv = ivParam.GetIV();
+                byte[] iv = ivParam.GetIV();
 
                 if (iv.Length != blockSize)
-                {
                     throw new ArgumentException("initialisation vector must be the same length as block size");
-                }
 
                 Array.Copy(iv, 0, IV, 0, iv.Length);
 
@@ -112,29 +109,27 @@ namespace Org.BouncyCastle.Crypto.Modes
             return cipher.GetBlockSize();
         }
 
-        /**
-        * Process one block of input from the array in and write it to
-        * the out array.
-        *
-        * @param in the array containing the input data.
-        * @param inOff offset into the in array the data starts at.
-        * @param out the array the output data will be copied into.
-        * @param outOff the offset into the out array the output will start at.
-        * @exception DataLengthException if there isn't enough data in in, or
-        * space in out.
-        * @exception InvalidOperationException if the cipher isn't initialised.
-        * @return the number of bytes processed and produced.
-        */
-        public int ProcessBlock(
-            byte[]	input,
-            int		inOff,
-            byte[]	output,
-            int		outOff)
+        public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
+        {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return encrypting
+                ? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff))
+                : DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+#else
+            return encrypting
+				? EncryptBlock(input, inOff, output, outOff)
+				: DecryptBlock(input, inOff, output, outOff);
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
-            return (encrypting)
-				?	EncryptBlock(input, inOff, output, outOff)
-				:	DecryptBlock(input, inOff, output, outOff);
+            return encrypting
+                ? EncryptBlock(input, output)
+                : DecryptBlock(input, output);
         }
+#endif
 
         /**
         * reset the chaining vector back to the IV and reset the underlying
@@ -148,33 +143,50 @@ namespace Org.BouncyCastle.Crypto.Modes
             cipher.Reset();
         }
 
-        /**
-        * Do the appropriate chaining step for CBC mode encryption.
-        *
-        * @param in the array containing the data to be encrypted.
-        * @param inOff offset into the in array the data starts at.
-        * @param out the array the encrypted data will be copied into.
-        * @param outOff the offset into the out array the output will start at.
-        * @exception DataLengthException if there isn't enough data in in, or
-        * space in out.
-        * @exception InvalidOperationException if the cipher isn't initialised.
-        * @return the number of bytes processed and produced.
-        */
-        private int EncryptBlock(
-            byte[]      input,
-            int         inOff,
-            byte[]      outBytes,
-            int         outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            Check.DataLength(input, blockSize, "input buffer too short");
+            Check.OutputLength(output, blockSize, "output buffer too short");
+
+            for (int i = 0; i < blockSize; i++)
+            {
+                cbcV[i] ^= input[i];
+            }
+
+            int length = cipher.ProcessBlock(cbcV, output);
+
+            output[..blockSize].CopyTo(cbcV);
+
+            return length;
+        }
+
+        private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
-            if ((inOff + blockSize) > input.Length)
+            Check.DataLength(input, blockSize, "input buffer too short");
+            Check.OutputLength(output, blockSize, "output buffer too short");
+
+            input[..blockSize].CopyTo(cbcNextV);
+
+            int length = cipher.ProcessBlock(input, output);
+
+            for (int i = 0; i < blockSize; i++)
             {
-                throw new DataLengthException("input buffer too short");
+                output[i] ^= cbcV[i];
             }
 
-            /*
-            * XOR the cbcV and the input,
-            * then encrypt the cbcV
-            */
+            byte[] tmp = cbcV;
+            cbcV = cbcNextV;
+            cbcNextV = tmp;
+
+            return length;
+        }
+#else
+        private int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
+        {
+            Check.DataLength(input, inOff, blockSize, "input buffer too short");
+            Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
+
             for (int i = 0; i < blockSize; i++)
             {
                 cbcV[i] ^= input[inOff + i];
@@ -182,60 +194,31 @@ namespace Org.BouncyCastle.Crypto.Modes
 
             int length = cipher.ProcessBlock(cbcV, 0, outBytes, outOff);
 
-            /*
-            * copy ciphertext to cbcV
-            */
             Array.Copy(outBytes, outOff, cbcV, 0, cbcV.Length);
 
             return length;
         }
 
-        /**
-        * Do the appropriate chaining step for CBC mode decryption.
-        *
-        * @param in the array containing the data to be decrypted.
-        * @param inOff offset into the in array the data starts at.
-        * @param out the array the decrypted data will be copied into.
-        * @param outOff the offset into the out array the output will start at.
-        * @exception DataLengthException if there isn't enough data in in, or
-        * space in out.
-        * @exception InvalidOperationException if the cipher isn't initialised.
-        * @return the number of bytes processed and produced.
-        */
-        private int DecryptBlock(
-            byte[]      input,
-            int         inOff,
-            byte[]      outBytes,
-            int         outOff)
+        private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
         {
-            if ((inOff + blockSize) > input.Length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
+            Check.DataLength(input, inOff, blockSize, "input buffer too short");
+            Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
 
             Array.Copy(input, inOff, cbcNextV, 0, blockSize);
 
             int length = cipher.ProcessBlock(input, inOff, outBytes, outOff);
 
-            /*
-            * XOR the cbcV and the output
-            */
             for (int i = 0; i < blockSize; i++)
             {
                 outBytes[outOff + i] ^= cbcV[i];
             }
 
-            /*
-            * swap the back up buffer into next position
-            */
-            byte[]  tmp;
-
-            tmp = cbcV;
+            byte[] tmp = cbcV;
             cbcV = cbcNextV;
             cbcNextV = tmp;
 
             return length;
         }
+#endif
     }
-
 }
diff --git a/crypto/src/crypto/modes/CcmBlockCipher.cs b/crypto/src/crypto/modes/CcmBlockCipher.cs
index abd7dbb8d..256cc1b13 100644
--- a/crypto/src/crypto/modes/CcmBlockCipher.cs
+++ b/crypto/src/crypto/modes/CcmBlockCipher.cs
@@ -117,6 +117,14 @@ namespace Org.BouncyCastle.Crypto.Modes
             associatedText.Write(inBytes, inOff, len);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
+        {
+            // TODO: Process AAD online
+            associatedText.Write(input);
+        }
+#endif
+
         public virtual int ProcessByte(
             byte	input,
             byte[]	outBytes,
@@ -134,7 +142,7 @@ namespace Org.BouncyCastle.Crypto.Modes
             byte[]	outBytes,
             int		outOff)
         {
-            Check.DataLength(inBytes, inOff, inLen, "Input buffer too short");
+            Check.DataLength(inBytes, inOff, inLen, "input buffer too short");
 
             data.Write(inBytes, inOff, inLen);
 
diff --git a/crypto/src/crypto/modes/CfbBlockCipher.cs b/crypto/src/crypto/modes/CfbBlockCipher.cs
index ed0be407a..bcbffcfb6 100644
--- a/crypto/src/crypto/modes/CfbBlockCipher.cs
+++ b/crypto/src/crypto/modes/CfbBlockCipher.cs
@@ -108,56 +108,76 @@ namespace Org.BouncyCastle.Crypto.Modes
             return blockSize;
         }
 
-		/**
-        * Process one block of input from the array in and write it to
-        * the out array.
-        *
-        * @param in the array containing the input data.
-        * @param inOff offset into the in array the data starts at.
-        * @param out the array the output data will be copied into.
-        * @param outOff the offset into the out array the output will start at.
-        * @exception DataLengthException if there isn't enough data in in, or
-        * space in out.
-        * @exception InvalidOperationException if the cipher isn't initialised.
-        * @return the number of bytes processed and produced.
-        */
-        public int ProcessBlock(
-            byte[]	input,
-            int		inOff,
-            byte[]	output,
-            int		outOff)
+        public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
-            return (encrypting)
-				?	EncryptBlock(input, inOff, output, outOff)
-				:	DecryptBlock(input, inOff, output, outOff);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return encrypting
+                ? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff))
+                : DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+#else
+            return encrypting
+				? EncryptBlock(input, inOff, output, outOff)
+				: DecryptBlock(input, inOff, output, outOff);
+#endif
         }
 
-		/**
-        * Do the appropriate processing for CFB mode encryption.
-        *
-        * @param in the array containing the data to be encrypted.
-        * @param inOff offset into the in array the data starts at.
-        * @param out the array the encrypted data will be copied into.
-        * @param outOff the offset into the out array the output will start at.
-        * @exception DataLengthException if there isn't enough data in in, or
-        * space in out.
-        * @exception InvalidOperationException if the cipher isn't initialised.
-        * @return the number of bytes processed and produced.
-        */
-        public int EncryptBlock(
-            byte[]      input,
-            int         inOff,
-            byte[]      outBytes,
-            int         outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
-            if ((inOff + blockSize) > input.Length)
+            return encrypting
+                ? EncryptBlock(input, output)
+                : DecryptBlock(input, output);
+        }
+#endif
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            Check.DataLength(input, blockSize, "input buffer too short");
+            Check.OutputLength(output, blockSize, "output buffer too short");
+
+            cipher.ProcessBlock(cfbV, cfbOutV);
+            //
+            // XOR the cfbV with the plaintext producing the ciphertext
+            //
+            for (int i = 0; i < blockSize; i++)
             {
-                throw new DataLengthException("input buffer too short");
+                output[i] = (byte)(cfbOutV[i] ^ input[i]);
             }
-            if ((outOff + blockSize) > outBytes.Length)
+            //
+            // change over the input block.
+            //
+            Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize);
+            output[..blockSize].CopyTo(cfbV.AsSpan(cfbV.Length - blockSize));
+            return blockSize;
+        }
+
+        public int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            Check.DataLength(input, blockSize, "input buffer too short");
+            Check.OutputLength(output, blockSize, "output buffer too short");
+
+            cipher.ProcessBlock(cfbV, 0, cfbOutV, 0);
+            //
+            // change over the input block.
+            //
+            Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize);
+            input[..blockSize].CopyTo(cfbV.AsSpan(cfbV.Length - blockSize));
+            //
+            // XOR the cfbV with the ciphertext producing the plaintext
+            //
+            for (int i = 0; i < blockSize; i++)
             {
-                throw new DataLengthException("output buffer too short");
+                output[i] = (byte)(cfbOutV[i] ^ input[i]);
             }
+            return blockSize;
+        }
+#else
+        public int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
+        {
+            Check.DataLength(input, inOff, blockSize, "input buffer too short");
+            Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
+
             cipher.ProcessBlock(cfbV, 0, cfbOutV, 0);
             //
             // XOR the cfbV with the plaintext producing the ciphertext
@@ -173,32 +193,12 @@ namespace Org.BouncyCastle.Crypto.Modes
             Array.Copy(outBytes, outOff, cfbV, cfbV.Length - blockSize, blockSize);
             return blockSize;
         }
-        /**
-        * Do the appropriate processing for CFB mode decryption.
-        *
-        * @param in the array containing the data to be decrypted.
-        * @param inOff offset into the in array the data starts at.
-        * @param out the array the encrypted data will be copied into.
-        * @param outOff the offset into the out array the output will start at.
-        * @exception DataLengthException if there isn't enough data in in, or
-        * space in out.
-        * @exception InvalidOperationException if the cipher isn't initialised.
-        * @return the number of bytes processed and produced.
-        */
-        public int DecryptBlock(
-            byte[]	input,
-            int		inOff,
-            byte[]	outBytes,
-            int		outOff)
+
+        public int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
         {
-            if ((inOff + blockSize) > input.Length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
-            if ((outOff + blockSize) > outBytes.Length)
-            {
-                throw new DataLengthException("output buffer too short");
-            }
+            Check.DataLength(input, inOff, blockSize, "input buffer too short");
+            Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
+
             cipher.ProcessBlock(cfbV, 0, cfbOutV, 0);
             //
             // change over the input block.
@@ -214,6 +214,8 @@ namespace Org.BouncyCastle.Crypto.Modes
             }
             return blockSize;
         }
+#endif
+
         /**
         * reset the chaining vector back to the IV and reset the underlying
         * cipher.
diff --git a/crypto/src/crypto/modes/ChaCha20Poly1305.cs b/crypto/src/crypto/modes/ChaCha20Poly1305.cs
index 462013200..385977fd5 100644
--- a/crypto/src/crypto/modes/ChaCha20Poly1305.cs
+++ b/crypto/src/crypto/modes/ChaCha20Poly1305.cs
@@ -209,6 +209,19 @@ namespace Org.BouncyCastle.Crypto.Modes
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
+        {
+            CheckAad();
+
+            if (!input.IsEmpty)
+            {
+                this.mAadCount = IncrementCount(mAadCount, (uint)input.Length, AadLimit);
+                mPoly1305.BlockUpdate(input);
+            }
+        }
+#endif
+
         public virtual int ProcessByte(byte input, byte[] outBytes, int outOff)
         {
             CheckData();
diff --git a/crypto/src/crypto/modes/EAXBlockCipher.cs b/crypto/src/crypto/modes/EAXBlockCipher.cs
index 624f385b5..ffe32ec68 100644
--- a/crypto/src/crypto/modes/EAXBlockCipher.cs
+++ b/crypto/src/crypto/modes/EAXBlockCipher.cs
@@ -194,13 +194,22 @@ namespace Org.BouncyCastle.Crypto.Modes
         public virtual void ProcessAadBytes(byte[] inBytes, int inOff, int len)
         {
             if (cipherInitialized)
-            {
                 throw new InvalidOperationException("AAD data cannot be added after encryption/decryption processing has begun.");
-            }
+
             mac.BlockUpdate(inBytes, inOff, len);
         }
 
-        public virtual int ProcessByte(
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
+		{
+			if (cipherInitialized)
+				throw new InvalidOperationException("AAD data cannot be added after encryption/decryption processing has begun.");
+
+			mac.BlockUpdate(input);
+		}
+#endif
+
+		public virtual int ProcessByte(
 			byte	input,
 			byte[]	outBytes,
 			int		outOff)
diff --git a/crypto/src/crypto/modes/GCMBlockCipher.cs b/crypto/src/crypto/modes/GCMBlockCipher.cs
index ac54e9762..bf9c14e28 100644
--- a/crypto/src/crypto/modes/GCMBlockCipher.cs
+++ b/crypto/src/crypto/modes/GCMBlockCipher.cs
@@ -278,6 +278,9 @@ namespace Org.BouncyCastle.Crypto.Modes
 
         public virtual void ProcessAadBytes(byte[] inBytes, int inOff, int len)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            ProcessAadBytes(inBytes.AsSpan(inOff, len));
+#else
             CheckStatus();
 
             if (atBlockPos > 0)
@@ -309,7 +312,42 @@ namespace Org.BouncyCastle.Crypto.Modes
 
             atBlockPos = BlockSize + inLimit - inOff;
             Array.Copy(inBytes, inOff, atBlock, 0, atBlockPos);
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
+        {
+            CheckStatus();
+
+            if (atBlockPos > 0)
+            {
+                int available = BlockSize - atBlockPos;
+                if (input.Length < available)
+                {
+                    input.CopyTo(atBlock.AsSpan(atBlockPos));
+                    atBlockPos += input.Length;
+                    return;
+                }
+
+                input[..available].CopyTo(atBlock.AsSpan(atBlockPos));
+                gHASHBlock(S_at, atBlock);
+                atLength += BlockSize;
+                input = input[available..];
+                //atBlockPos = 0;
+            }
+
+            while (input.Length >= BlockSize)
+            {
+                gHASHBlock(S_at, input);
+                atLength += BlockSize;
+                input = input[BlockSize..];
+            }
+
+            input.CopyTo(atBlock);
+            atBlockPos = input.Length;
         }
+#endif
 
         private void InitCipher()
         {
@@ -930,6 +968,13 @@ namespace Org.BouncyCastle.Crypto.Modes
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private void gHASHBlock(byte[] Y, ReadOnlySpan<byte> b)
+        {
+            GcmUtilities.Xor(Y, b);
+            multiplier.MultiplyH(Y);
+        }
+#else
         private void gHASHBlock(byte[] Y, byte[] b)
         {
             GcmUtilities.Xor(Y, b);
@@ -941,6 +986,7 @@ namespace Org.BouncyCastle.Crypto.Modes
             GcmUtilities.Xor(Y, b, off);
             multiplier.MultiplyH(Y);
         }
+#endif
 
         private void gHASHPartial(byte[] Y, byte[] b, int off, int len)
         {
diff --git a/crypto/src/crypto/modes/GOFBBlockCipher.cs b/crypto/src/crypto/modes/GOFBBlockCipher.cs
index 436b58a1d..4c8576a58 100644
--- a/crypto/src/crypto/modes/GOFBBlockCipher.cs
+++ b/crypto/src/crypto/modes/GOFBBlockCipher.cs
@@ -1,7 +1,7 @@
 using System;
 
-using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Modes
 {
@@ -131,41 +131,17 @@ namespace Org.BouncyCastle.Crypto.Modes
 			return blockSize;
 		}
 
-		/**
-		* Process one block of input from the array in and write it to
-		* the out array.
-		*
-		* @param in the array containing the input data.
-		* @param inOff offset into the in array the data starts at.
-		* @param out the array the output data will be copied into.
-		* @param outOff the offset into the out array the output will start at.
-		* @exception DataLengthException if there isn't enough data in in, or
-		* space in out.
-		* @exception InvalidOperationException if the cipher isn't initialised.
-		* @return the number of bytes processed and produced.
-		*/
-		public int ProcessBlock(
-			byte[]	input,
-			int		inOff,
-			byte[]	output,
-			int		outOff)
+		public int ProcessBlock(byte[] input, int inOff, byte[]	output, int outOff)
 		{
-			if ((inOff + blockSize) > input.Length)
-			{
-				throw new DataLengthException("input buffer too short");
-			}
-
-			if ((outOff + blockSize) > output.Length)
-			{
-				throw new DataLengthException("output buffer too short");
-			}
+			Check.DataLength(input, inOff, blockSize, "input buffer too short");
+			Check.OutputLength(output, outOff, blockSize, "output buffer too short");
 
 			if (firstStep)
 			{
 				firstStep = false;
 				cipher.ProcessBlock(ofbV, 0, ofbOutV, 0);
-				N3 = bytesToint(ofbOutV, 0);
-				N4 = bytesToint(ofbOutV, 4);
+				N3 = (int)Pack.LE_To_UInt32(ofbOutV, 0);
+				N4 = (int)Pack.LE_To_UInt32(ofbOutV, 4);
 			}
 			N3 += C2;
 			N4 += C1;
@@ -176,8 +152,8 @@ namespace Org.BouncyCastle.Crypto.Modes
                     N4++;
                 }
             }
-            intTobytes(N3, ofbV, 0);
-			intTobytes(N4, ofbV, 4);
+			Pack.UInt32_To_LE((uint)N3, ofbV, 0);
+			Pack.UInt32_To_LE((uint)N4, ofbV, 4);
 
 			cipher.ProcessBlock(ofbV, 0, ofbOutV, 0);
 
@@ -199,6 +175,52 @@ namespace Org.BouncyCastle.Crypto.Modes
 			return blockSize;
 		}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+		{
+			Check.DataLength(input, blockSize, "input buffer too short");
+			Check.OutputLength(output, blockSize, "output buffer too short");
+
+			if (firstStep)
+			{
+				firstStep = false;
+				cipher.ProcessBlock(ofbV, ofbOutV);
+				N3 = (int)Pack.LE_To_UInt32(ofbOutV, 0);
+				N4 = (int)Pack.LE_To_UInt32(ofbOutV, 4);
+			}
+			N3 += C2;
+			N4 += C1;
+			if (N4 < C1)  // addition is mod (2**32 - 1)
+			{
+				if (N4 > 0)
+				{
+					N4++;
+				}
+			}
+			Pack.UInt32_To_LE((uint)N3, ofbV, 0);
+			Pack.UInt32_To_LE((uint)N4, ofbV, 4);
+
+			cipher.ProcessBlock(ofbV, ofbOutV);
+
+			//
+			// XOR the ofbV with the plaintext producing the cipher text (and
+			// the next input block).
+			//
+			for (int i = 0; i < blockSize; i++)
+			{
+				output[i] = (byte)(ofbOutV[i] ^ input[i]);
+			}
+
+			//
+			// change over the input block.
+			//
+			Array.Copy(ofbV, blockSize, ofbV, 0, ofbV.Length - blockSize);
+			Array.Copy(ofbOutV, 0, ofbV, ofbV.Length - blockSize, blockSize);
+
+			return blockSize;
+		}
+#endif
+
 		/**
 		* reset the feedback vector back to the IV and reset the underlying
 		* cipher.
@@ -209,26 +231,5 @@ namespace Org.BouncyCastle.Crypto.Modes
 
 			cipher.Reset();
 		}
-
-		//array of bytes to type int
-		private int bytesToint(
-			byte[]  inBytes,
-			int     inOff)
-		{
-			return  (int)((inBytes[inOff + 3] << 24) & 0xff000000) + ((inBytes[inOff + 2] << 16) & 0xff0000) +
-					((inBytes[inOff + 1] << 8) & 0xff00) + (inBytes[inOff] & 0xff);
-		}
-
-		//int to array of bytes
-		private void intTobytes(
-				int     num,
-				byte[]  outBytes,
-				int     outOff)
-		{
-				outBytes[outOff + 3] = (byte)(num >> 24);
-				outBytes[outOff + 2] = (byte)(num >> 16);
-				outBytes[outOff + 1] = (byte)(num >> 8);
-				outBytes[outOff] =     (byte)num;
-		}
 	}
 }
diff --git a/crypto/src/crypto/modes/GcmSivBlockCipher.cs b/crypto/src/crypto/modes/GcmSivBlockCipher.cs
index 5af3429f2..284a952a6 100644
--- a/crypto/src/crypto/modes/GcmSivBlockCipher.cs
+++ b/crypto/src/crypto/modes/GcmSivBlockCipher.cs
@@ -1,7 +1,6 @@
 using System;
 using System.IO;
 
-using Org.BouncyCastle.Crypto.Engines;
 using Org.BouncyCastle.Crypto.Modes.Gcm;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Crypto.Utilities;
@@ -296,16 +295,31 @@ namespace Org.BouncyCastle.Crypto.Modes
 
         public virtual void ProcessAadBytes(byte[] pData, int pOffset, int pLen)
         {
-            /* Check that we can supply AEAD */
-            CheckAeadStatus(pLen);
-
             /* Check input buffer */
             CheckBuffer(pData, pOffset, pLen, false);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            ProcessAadBytes(pData.AsSpan(pOffset, pLen));
+#else
+            /* Check that we can supply AEAD */
+            CheckAeadStatus(pLen);
+
             /* Process the aead */
             theAEADHasher.updateHash(pData, pOffset, pLen);
+#endif
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
+        {
+            /* Check that we can supply AEAD */
+            CheckAeadStatus(input.Length);
+
+            /* Process the aead */
+            theAEADHasher.updateHash(input);
+        }
+#endif
+
         public virtual int ProcessByte(byte pByte, byte[] pOutput, int pOutOffset)
         {
             /* Check that we have initialised */
@@ -472,8 +486,8 @@ namespace Org.BouncyCastle.Crypto.Modes
             if (badLen || myLast > myBufLen)
             {
                 throw pOutput
-                ? new OutputLengthException("Output buffer too short.")
-                : new DataLengthException("Input buffer too short.");
+                ? new OutputLengthException("output buffer too short.")
+                : new DataLengthException("input buffer too short.");
             }
         }
 
@@ -647,6 +661,18 @@ namespace Org.BouncyCastle.Crypto.Modes
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private static void fillReverse(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            /* Loop through the buffer */
+            for (int i = 0, j = BUFLEN - 1; i < input.Length; i++, j--)
+            {
+                /* Copy byte */
+                output[j] = input[i];
+            }
+        }
+#endif
+
         /**
         * xor a full block buffer.
         * @param pLeft the left operand and result
@@ -875,8 +901,8 @@ namespace Org.BouncyCastle.Crypto.Modes
                     parent.gHASH(parent.theReverse);
 
                     /* Adjust counters */
-                    numProcessed += mySpace;
-                    myRemaining -= mySpace;
+                    numProcessed += BUFLEN;
+                    myRemaining -= BUFLEN;
                 }
 
                 /* If we have remaining data */
@@ -891,6 +917,49 @@ namespace Org.BouncyCastle.Crypto.Modes
                 numHashed += (ulong)pLen;
             }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            internal void updateHash(ReadOnlySpan<byte> buffer)
+            {
+                int pLen = buffer.Length;
+
+                /* If we should process the cache */
+                int mySpace = BUFLEN - numActive;
+                if (numActive > 0 && buffer.Length >= mySpace)
+                {
+                    /* Copy data into the cache and hash it */
+                    buffer[..mySpace].CopyTo(theBuffer.AsSpan(numActive));
+                    fillReverse(theBuffer, parent.theReverse);
+                    parent.gHASH(parent.theReverse);
+
+                    /* Adjust counters */
+                    buffer = buffer[mySpace..];
+                    numActive = 0;
+                }
+
+                /* While we have full blocks */
+                while (buffer.Length >= BUFLEN)
+                {
+                    /* Access the next data */
+                    fillReverse(buffer[..BUFLEN], parent.theReverse);
+                    parent.gHASH(parent.theReverse);
+
+                    /* Adjust counters */
+                    buffer = buffer[BUFLEN..];
+                }
+
+                /* If we have remaining data */
+                if (!buffer.IsEmpty)
+                {
+                    /* Copy data into the cache */
+                    buffer.CopyTo(theBuffer.AsSpan(numActive));
+                    numActive += buffer.Length;
+                }
+
+                /* Adjust the number of bytes processed */
+                numHashed += (ulong)pLen;
+            }
+#endif
+
             /**
             * complete hash.
             */
diff --git a/crypto/src/crypto/modes/IAeadCipher.cs b/crypto/src/crypto/modes/IAeadCipher.cs
index 437693cb6..c61e13b01 100644
--- a/crypto/src/crypto/modes/IAeadCipher.cs
+++ b/crypto/src/crypto/modes/IAeadCipher.cs
@@ -41,6 +41,13 @@ namespace Org.BouncyCastle.Crypto.Modes
         /// <param name="len">The number of bytes to be processed.</param>
         void ProcessAadBytes(byte[] inBytes, int inOff, int len);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        /// <summary>Add a span of bytes to the associated data check.</summary>
+        /// <remarks>If the implementation supports it, this will be an online operation and will not retain the associated data.</remarks>
+        /// <param name="input">the span containing the data.</param>
+        void ProcessAadBytes(ReadOnlySpan<byte> input);
+#endif
+
         /**
 		* Encrypt/decrypt a single byte.
 		*
diff --git a/crypto/src/crypto/modes/KCcmBlockCipher.cs b/crypto/src/crypto/modes/KCcmBlockCipher.cs
index afa7063a3..afa68a794 100644
--- a/crypto/src/crypto/modes/KCcmBlockCipher.cs
+++ b/crypto/src/crypto/modes/KCcmBlockCipher.cs
@@ -158,6 +158,13 @@ namespace Org.BouncyCastle.Crypto.Modes
             associatedText.Write(input, inOff, len);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
+        {
+            associatedText.Write(input);
+        }
+#endif
+
         private void ProcessAAD(byte[] assocText, int assocOff, int assocLen, int dataLen)
         {
             if (assocLen - assocOff < engine.GetBlockSize())
diff --git a/crypto/src/crypto/modes/KCtrBlockCipher.cs b/crypto/src/crypto/modes/KCtrBlockCipher.cs
index ff0249a6c..79b74f84c 100644
--- a/crypto/src/crypto/modes/KCtrBlockCipher.cs
+++ b/crypto/src/crypto/modes/KCtrBlockCipher.cs
@@ -118,25 +118,30 @@ namespace Org.BouncyCastle.Crypto.Modes
 
         public void ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
         {
-            if (outOff + len > output.Length)
-            {
-               throw new DataLengthException("Output buffer too short");
-            }
-
-            if (inOff + len > input.Length)
-            {
-                    throw new DataLengthException("Input buffer too small");
-            }
+            Check.DataLength(input, inOff, len, "input buffer too small");
+            Check.OutputLength(output, outOff, len, "output buffer too short");
 
             int inStart = inOff;
             int inEnd = inOff + len;
             int outStart = outOff;
 
-            while (inStart<inEnd)
+            while (inStart < inEnd)
+            {
+                output[outStart++] = CalculateByte(input[inStart++]);
+            }
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            Check.OutputLength(output, input.Length, "output buffer too short");
+
+            for (int i = 0; i < input.Length; ++i)
             {
-                 output[outStart++] = CalculateByte(input[inStart++]);
+                output[i] = CalculateByte(input[i]);
             }
         }
+#endif
 
         protected byte CalculateByte(byte b)
         {
@@ -176,19 +181,27 @@ namespace Org.BouncyCastle.Crypto.Modes
          */
         public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
-            if (input.Length - inOff< GetBlockSize())
-            {
-                throw new DataLengthException("Input buffer too short");
-            }
-            if (output.Length - outOff< GetBlockSize())
-            {
-                throw new DataLengthException("Output buffer too short");
-            }
+            int blockSize = GetBlockSize();
+            Check.DataLength(input, inOff, blockSize, "input buffer too short");
+            Check.OutputLength(output, outOff, blockSize, "output buffer too short");
+
+            ProcessBytes(input, inOff, blockSize, output, outOff);
+
+            return blockSize;
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            int blockSize = GetBlockSize();
+            Check.DataLength(input, blockSize, "input buffer too short");
+            Check.OutputLength(output, blockSize, "output buffer too short");
 
-            ProcessBytes(input, inOff, GetBlockSize(), output, outOff);
+            ProcessBytes(input[..blockSize], output);
 
-            return GetBlockSize();
+            return blockSize;
         }
+#endif
 
         /**
         * reset the chaining vector back to the IV and reset the underlying
diff --git a/crypto/src/crypto/modes/OCBBlockCipher.cs b/crypto/src/crypto/modes/OCBBlockCipher.cs
index 28e88a6c9..db6aa39ae 100644
--- a/crypto/src/crypto/modes/OCBBlockCipher.cs
+++ b/crypto/src/crypto/modes/OCBBlockCipher.cs
@@ -287,6 +287,20 @@ namespace Org.BouncyCastle.Crypto.Modes
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
+        {
+            for (int i = 0; i < input.Length; ++i)
+            {
+                hashBlock[hashBlockPos] = input[i];
+                if (++hashBlockPos == hashBlock.Length)
+                {
+                    ProcessHashBlock();
+                }
+            }
+        }
+#endif
+
         public virtual int ProcessByte(byte input, byte[] output, int outOff)
         {
             mainBlock[mainBlockPos] = input;
diff --git a/crypto/src/crypto/modes/OfbBlockCipher.cs b/crypto/src/crypto/modes/OfbBlockCipher.cs
index a99f8c5d7..ac9b9a06c 100644
--- a/crypto/src/crypto/modes/OfbBlockCipher.cs
+++ b/crypto/src/crypto/modes/OfbBlockCipher.cs
@@ -61,9 +61,8 @@ namespace Org.BouncyCastle.Crypto.Modes
             bool				forEncryption, //ignored by this OFB mode
             ICipherParameters	parameters)
         {
-			if (parameters is ParametersWithIV)
+			if (parameters is ParametersWithIV ivParam)
             {
-                ParametersWithIV ivParam = (ParametersWithIV)parameters;
                 byte[] iv = ivParam.GetIV();
 
                 if (iv.Length < IV.Length)
@@ -118,36 +117,38 @@ namespace Org.BouncyCastle.Crypto.Modes
             return blockSize;
         }
 
-        /**
-        * Process one block of input from the array in and write it to
-        * the out array.
-        *
-        * @param in the array containing the input data.
-        * @param inOff offset into the in array the data starts at.
-        * @param out the array the output data will be copied into.
-        * @param outOff the offset into the out array the output will start at.
-        * @exception DataLengthException if there isn't enough data in in, or
-        * space in out.
-        * @exception InvalidOperationException if the cipher isn't initialised.
-        * @return the number of bytes processed and produced.
-        */
-        public int ProcessBlock(
-            byte[]	input,
-            int		inOff,
-            byte[]	output,
-            int		outOff)
+        public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
-            if ((inOff + blockSize) > input.Length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
+            Check.DataLength(input, inOff, blockSize, "input buffer too short");
+            Check.OutputLength(output, outOff, blockSize, "output buffer too short");
+
+            cipher.ProcessBlock(ofbV, 0, ofbOutV, 0);
 
-            if ((outOff + blockSize) > output.Length)
+            //
+            // XOR the ofbV with the plaintext producing the cipher text (and
+            // the next input block).
+            //
+            for (int i = 0; i < blockSize; i++)
             {
-                throw new DataLengthException("output buffer too short");
+                output[outOff + i] = (byte)(ofbOutV[i] ^ input[inOff + i]);
             }
 
-            cipher.ProcessBlock(ofbV, 0, ofbOutV, 0);
+            //
+            // change over the input block.
+            //
+            Array.Copy(ofbV, blockSize, ofbV, 0, ofbV.Length - blockSize);
+            Array.Copy(ofbOutV, 0, ofbV, ofbV.Length - blockSize, blockSize);
+
+            return blockSize;
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            Check.DataLength(input, blockSize, "input buffer too short");
+            Check.OutputLength(output, blockSize, "output buffer too short");
+
+            cipher.ProcessBlock(ofbV, ofbOutV);
 
             //
             // XOR the ofbV with the plaintext producing the cipher text (and
@@ -155,7 +156,7 @@ namespace Org.BouncyCastle.Crypto.Modes
             //
             for (int i = 0; i < blockSize; i++)
             {
-                output[outOff + i] = (byte)(ofbOutV[i] ^ input[inOff + i]);
+                output[i] = (byte)(ofbOutV[i] ^ input[i]);
             }
 
             //
@@ -166,6 +167,7 @@ namespace Org.BouncyCastle.Crypto.Modes
 
             return blockSize;
         }
+#endif
 
         /**
         * reset the feedback vector back to the IV and reset the underlying
diff --git a/crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs b/crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs
index 038ca783d..45998248c 100644
--- a/crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs
+++ b/crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs
@@ -79,29 +79,29 @@ namespace Org.BouncyCastle.Crypto.Modes
             return cipher.GetBlockSize();
         }
 
-		/**
-        * Process one block of input from the array in and write it to
-        * the out array.
-        *
-        * @param in the array containing the input data.
-        * @param inOff offset into the in array the data starts at.
-        * @param out the array the output data will be copied into.
-        * @param outOff the offset into the out array the output will start at.
-        * @exception DataLengthException if there isn't enough data in in, or
-        * space in out.
-        * @exception InvalidOperationException if the cipher isn't initialised.
-        * @return the number of bytes processed and produced.
-        */
-        public int ProcessBlock(
-            byte[]	input,
-            int		inOff,
-            byte[]	output,
-            int		outOff)
+        public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
-            return (forEncryption) ? EncryptBlock(input, inOff, output, outOff) : DecryptBlock(input, inOff, output, outOff);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return forEncryption
+                ? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff))
+                : DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
+#else
+            return forEncryption
+                ? EncryptBlock(input, inOff, output, outOff)
+                : DecryptBlock(input, inOff, output, outOff);
+#endif
         }
 
-		/**
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            return forEncryption
+                ? EncryptBlock(input, output)
+                : DecryptBlock(input, output);
+        }
+#endif
+
+        /**
         * reset the chaining vector back to the IV and reset the underlying
         * cipher.
         */
@@ -125,15 +125,12 @@ namespace Org.BouncyCastle.Crypto.Modes
         * @exception ArgumentException if the parameters argument is
         * inappropriate.
         */
-        public void Init(
-            bool forEncryption,
-            ICipherParameters parameters)
+        public void Init(bool forEncryption, ICipherParameters parameters)
         {
             this.forEncryption = forEncryption;
 
-            if (parameters is ParametersWithIV)
+            if (parameters is ParametersWithIV ivParam)
             {
-                ParametersWithIV ivParam = (ParametersWithIV)parameters;
                 byte[] iv = ivParam.GetIV();
 
                 if (iv.Length < IV.Length)
@@ -169,34 +166,132 @@ namespace Org.BouncyCastle.Crypto.Modes
             return (byte)(FRE[blockOff] ^ data);
         }
 
-		/**
-        * Do the appropriate processing for CFB IV mode encryption.
-        *
-        * @param in the array containing the data to be encrypted.
-        * @param inOff offset into the in array the data starts at.
-        * @param out the array the encrypted data will be copied into.
-        * @param outOff the offset into the out array the output will start at.
-        * @exception DataLengthException if there isn't enough data in in, or
-        * space in out.
-        * @exception InvalidOperationException if the cipher isn't initialised.
-        * @return the number of bytes processed and produced.
-        */
-        private int EncryptBlock(
-            byte[]	input,
-            int		inOff,
-            byte[]	outBytes,
-            int		outOff)
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
         {
-            if ((inOff + blockSize) > input.Length)
+            Check.DataLength(input, blockSize, "input buffer too short");
+            Check.OutputLength(output, blockSize, "output buffer too short");
+
+            if (count > blockSize)
             {
-                throw new DataLengthException("input buffer too short");
+                FR[blockSize - 2] = output[0] = EncryptByte(input[0], blockSize - 2);
+                FR[blockSize - 1] = output[1] = EncryptByte(input[1], blockSize - 1);
+
+                cipher.ProcessBlock(FR, FRE);
+
+                for (int n = 2; n < blockSize; n++)
+                {
+					FR[n - 2] = output[n] = EncryptByte(input[n], n - 2);
+                }
             }
+            else if (count == 0)
+            {
+                cipher.ProcessBlock(FR, FRE);
+
+				for (int n = 0; n < blockSize; n++)
+                {
+					FR[n] = output[n] = EncryptByte(input[n], n);
+                }
 
-            if ((outOff + blockSize) > outBytes.Length)
+				count += blockSize;
+            }
+            else if (count == blockSize)
             {
-                throw new DataLengthException("output buffer too short");
+                cipher.ProcessBlock(FR, FRE);
+
+                output[0] = EncryptByte(input[0], 0);
+                output[1] = EncryptByte(input[1], 1);
+
+                //
+                // do reset
+                //
+                Array.Copy(FR, 2, FR, 0, blockSize - 2);
+                output[..2].CopyTo(FR.AsSpan(blockSize - 2));
+
+                cipher.ProcessBlock(FR, FRE);
+
+                for (int n = 2; n < blockSize; n++)
+                {
+					FR[n - 2] = output[n] = EncryptByte(input[n], n - 2);
+                }
+
+				count += blockSize;
             }
 
+            return blockSize;
+        }
+
+        private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            Check.DataLength(input, blockSize, "input buffer too short");
+            Check.OutputLength(output, blockSize, "output buffer too short");
+
+            if (count > blockSize)
+            {
+				byte inVal = input[0];
+				FR[blockSize - 2] = inVal;
+				output[0] = EncryptByte(inVal, blockSize - 2);
+
+				inVal = input[1];
+				FR[blockSize - 1] = inVal;
+				output[1] = EncryptByte(inVal, blockSize - 1);
+
+                cipher.ProcessBlock(FR, FRE);
+
+                for (int n = 2; n < blockSize; n++)
+                {
+					inVal = input[n];
+					FR[n - 2] = inVal;
+					output[n] = EncryptByte(inVal, n - 2);
+				}
+            }
+            else if (count == 0)
+            {
+                cipher.ProcessBlock(FR, FRE);
+
+                for (int n = 0; n < blockSize; n++)
+                {
+                    FR[n] = input[n];
+                    output[n] = EncryptByte(input[n], n);
+                }
+
+                count += blockSize;
+            }
+            else if (count == blockSize)
+            {
+                cipher.ProcessBlock(FR, 0, FRE, 0);
+
+				byte inVal1 = input[0];
+				byte inVal2 = input[1];
+				output[0] = EncryptByte(inVal1, 0);
+				output[1] = EncryptByte(inVal2, 1);
+
+                Array.Copy(FR, 2, FR, 0, blockSize - 2);
+
+				FR[blockSize - 2] = inVal1;
+				FR[blockSize - 1] = inVal2;
+
+                cipher.ProcessBlock(FR, 0, FRE, 0);
+
+                for (int n = 2; n < blockSize; n++)
+                {
+					byte inVal = input[n];
+					FR[n - 2] = inVal;
+					output[n] = EncryptByte(inVal, n - 2);
+                }
+
+                count += blockSize;
+            }
+
+            return blockSize;
+        }
+#else
+        private int EncryptBlock(byte[]	input, int inOff, byte[] outBytes, int outOff)
+        {
+            Check.DataLength(input, inOff, blockSize, "input buffer too short");
+            Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
+
             if (count > blockSize)
             {
                 FR[blockSize - 2] = outBytes[outOff] = EncryptByte(input[inOff], blockSize - 2);
@@ -246,33 +341,10 @@ namespace Org.BouncyCastle.Crypto.Modes
             return blockSize;
         }
 
-        /**
-        * Do the appropriate processing for CFB IV mode decryption.
-        *
-        * @param in the array containing the data to be decrypted.
-        * @param inOff offset into the in array the data starts at.
-        * @param out the array the encrypted data will be copied into.
-        * @param outOff the offset into the out array the output will start at.
-        * @exception DataLengthException if there isn't enough data in in, or
-        * space in out.
-        * @exception InvalidOperationException if the cipher isn't initialised.
-        * @return the number of bytes processed and produced.
-        */
-        private int DecryptBlock(
-            byte[]	input,
-            int		inOff,
-            byte[]	outBytes,
-            int		outOff)
+        private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
         {
-            if ((inOff + blockSize) > input.Length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
-
-            if ((outOff + blockSize) > outBytes.Length)
-            {
-                throw new DataLengthException("output buffer too short");
-            }
+            Check.DataLength(input, inOff, blockSize, "input buffer too short");
+            Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
 
             if (count > blockSize)
             {
@@ -300,7 +372,7 @@ namespace Org.BouncyCastle.Crypto.Modes
                 for (int n = 0; n < blockSize; n++)
                 {
                     FR[n] = input[inOff + n];
-                    outBytes[n] = EncryptByte(input[inOff + n], n);
+                    outBytes[outOff + n] = EncryptByte(input[inOff + n], n);
                 }
 
                 count += blockSize;
@@ -333,5 +405,6 @@ namespace Org.BouncyCastle.Crypto.Modes
 
             return blockSize;
         }
+#endif
     }
 }
diff --git a/crypto/src/crypto/modes/SicBlockCipher.cs b/crypto/src/crypto/modes/SicBlockCipher.cs
index 0bea4a455..431e2952c 100644
--- a/crypto/src/crypto/modes/SicBlockCipher.cs
+++ b/crypto/src/crypto/modes/SicBlockCipher.cs
@@ -85,11 +85,7 @@ namespace Org.BouncyCastle.Crypto.Modes
             return cipher.GetBlockSize();
         }
 
-        public virtual int ProcessBlock(
-            byte[]	input,
-            int		inOff,
-            byte[]	output,
-            int		outOff)
+        public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
         {
             cipher.ProcessBlock(counter, 0, counterOut, 0);
 
@@ -110,6 +106,29 @@ namespace Org.BouncyCastle.Crypto.Modes
             return counter.Length;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            cipher.ProcessBlock(counter, 0, counterOut, 0);
+
+            //
+            // XOR the counterOut with the plaintext producing the cipher text
+            //
+            for (int i = 0; i < counterOut.Length; i++)
+            {
+                output[i] = (byte)(counterOut[i] ^ input[i]);
+            }
+
+            // Increment the counter
+            int j = counter.Length;
+            while (--j >= 0 && ++counter[j] == 0)
+            {
+            }
+
+            return counter.Length;
+        }
+#endif
+
         public virtual void Reset()
         {
             Arrays.Fill(counter, (byte)0);
diff --git a/crypto/src/crypto/modes/gcm/GcmUtilities.cs b/crypto/src/crypto/modes/gcm/GcmUtilities.cs
index 676d74107..78a1f0860 100644
--- a/crypto/src/crypto/modes/gcm/GcmUtilities.cs
+++ b/crypto/src/crypto/modes/gcm/GcmUtilities.cs
@@ -275,6 +275,21 @@ namespace Org.BouncyCastle.Crypto.Modes.Gcm
             z.n1 = x.n1 ^ y.n1;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal static void Xor(Span<byte> x, ReadOnlySpan<byte> y)
+        {
+            int i = 0;
+            do
+            {
+                x[i] ^= y[i]; ++i;
+                x[i] ^= y[i]; ++i;
+                x[i] ^= y[i]; ++i;
+                x[i] ^= y[i]; ++i;
+            }
+            while (i < 16);
+        }
+#endif
+
         private static ulong ImplMul64(ulong x, ulong y)
         {
             ulong x0 = x & 0x1111111111111111UL;
diff --git a/crypto/src/crypto/signers/DsaDigestSigner.cs b/crypto/src/crypto/signers/DsaDigestSigner.cs
index 15444a0f7..e8c2487ba 100644
--- a/crypto/src/crypto/signers/DsaDigestSigner.cs
+++ b/crypto/src/crypto/signers/DsaDigestSigner.cs
@@ -14,19 +14,12 @@ namespace Org.BouncyCastle.Crypto.Signers
         private readonly IDsaEncoding encoding;
         private bool forSigning;
 
-		public DsaDigestSigner(
-			IDsa	dsa,
-			IDigest	digest)
+		public DsaDigestSigner(IDsa dsa, IDigest digest)
+			: this(dsa, digest, StandardDsaEncoding.Instance)
 		{
-            this.dsa = dsa;
-            this.digest = digest;
-            this.encoding = StandardDsaEncoding.Instance;
 		}
 
-        public DsaDigestSigner(
-            IDsaExt dsa,
-            IDigest digest,
-            IDsaEncoding encoding)
+        public DsaDigestSigner(IDsa dsa, IDigest digest, IDsaEncoding encoding)
         {
             this.dsa = dsa;
             this.digest = digest;
@@ -38,17 +31,14 @@ namespace Org.BouncyCastle.Crypto.Signers
 			get { return digest.AlgorithmName + "with" + dsa.AlgorithmName; }
 		}
 
-        public virtual void Init(
-			bool forSigning,
-			ICipherParameters parameters)
+        public virtual void Init(bool forSigning, ICipherParameters parameters)
 		{
 			this.forSigning = forSigning;
 
 			AsymmetricKeyParameter k;
-
-			if (parameters is ParametersWithRandom)
+			if (parameters is ParametersWithRandom withRandom)
 			{
-				k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters;
+				k = (AsymmetricKeyParameter)withRandom.Parameters;
 			}
 			else
 			{
@@ -66,31 +56,24 @@ namespace Org.BouncyCastle.Crypto.Signers
 			dsa.Init(forSigning, parameters);
 		}
 
-		/**
-		 * update the internal digest with the byte b
-		 */
-        public virtual void Update(
-			byte input)
+        public virtual void Update(byte input)
 		{
 			digest.Update(input);
 		}
 
-		/**
-		 * update the internal digest with the byte array in
-		 */
-        public virtual void BlockUpdate(
-			byte[]	input,
-			int			inOff,
-			int			length)
+        public virtual void BlockUpdate(byte[] input, int inOff, int inLen)
+		{
+			digest.BlockUpdate(input, inOff, inLen);
+		}
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public virtual void BlockUpdate(ReadOnlySpan<byte> input)
 		{
-			digest.BlockUpdate(input, inOff, length);
+			digest.BlockUpdate(input);
 		}
+#endif
 
-		/**
-		 * Generate a signature for the message we've been loaded with using
-		 * the key we were initialised with.
-     */
-        public virtual byte[] GenerateSignature()
+		public virtual byte[] GenerateSignature()
 		{
 			if (!forSigning)
 				throw new InvalidOperationException("DSADigestSigner not initialised for signature generation.");
@@ -110,9 +93,7 @@ namespace Org.BouncyCastle.Crypto.Signers
             }
 		}
 
-		/// <returns>true if the internal state represents the signature described in the passed in array.</returns>
-        public virtual bool VerifySignature(
-			byte[] signature)
+        public virtual bool VerifySignature(byte[] signature)
 		{
 			if (forSigning)
 				throw new InvalidOperationException("DSADigestSigner not initialised for verification");
@@ -132,7 +113,6 @@ namespace Org.BouncyCastle.Crypto.Signers
             }
 		}
 
-		/// <summary>Reset the internal state</summary>
         public virtual void Reset()
 		{
 			digest.Reset();
@@ -140,7 +120,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 
         protected virtual BigInteger GetOrder()
         {
-            return dsa is IDsaExt ? ((IDsaExt)dsa).Order : null;
+            return dsa.Order;
         }
 	}
 }
diff --git a/crypto/src/crypto/signers/DsaSigner.cs b/crypto/src/crypto/signers/DsaSigner.cs
index 1f5d9b937..7799edc0e 100644
--- a/crypto/src/crypto/signers/DsaSigner.cs
+++ b/crypto/src/crypto/signers/DsaSigner.cs
@@ -12,7 +12,7 @@ namespace Org.BouncyCastle.Crypto.Signers
      * Cryptography", pages 452 - 453.
      */
     public class DsaSigner
-        : IDsaExt
+        : IDsa
     {
         protected readonly IDsaKCalculator kCalculator;
 
diff --git a/crypto/src/crypto/signers/ECDsaSigner.cs b/crypto/src/crypto/signers/ECDsaSigner.cs
index 0a265d96e..590c3236b 100644
--- a/crypto/src/crypto/signers/ECDsaSigner.cs
+++ b/crypto/src/crypto/signers/ECDsaSigner.cs
@@ -1,6 +1,5 @@
 using System;
 
-using Org.BouncyCastle.Crypto.Digests;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Math.EC;
@@ -14,7 +13,7 @@ namespace Org.BouncyCastle.Crypto.Signers
      * EC-DSA as described in X9.62
      */
     public class ECDsaSigner
-        : IDsaExt
+        : IDsa
     {
         private static readonly BigInteger Eight = BigInteger.ValueOf(8);
 
diff --git a/crypto/src/crypto/signers/ECGOST3410Signer.cs b/crypto/src/crypto/signers/ECGOST3410Signer.cs
index 7df43f0a0..7b3833b66 100644
--- a/crypto/src/crypto/signers/ECGOST3410Signer.cs
+++ b/crypto/src/crypto/signers/ECGOST3410Signer.cs
@@ -14,7 +14,7 @@ namespace Org.BouncyCastle.Crypto.Signers
      * GOST R 34.10-2001 Signature Algorithm
      */
     public class ECGost3410Signer
-        : IDsaExt
+        : IDsa
     {
         private ECKeyParameters key;
         private SecureRandom random;
diff --git a/crypto/src/crypto/signers/ECNRSigner.cs b/crypto/src/crypto/signers/ECNRSigner.cs
index bc193e797..b22d7a977 100644
--- a/crypto/src/crypto/signers/ECNRSigner.cs
+++ b/crypto/src/crypto/signers/ECNRSigner.cs
@@ -13,7 +13,7 @@ namespace Org.BouncyCastle.Crypto.Signers
      * EC-NR as described in IEEE 1363-2000
      */
     public class ECNRSigner
-        : IDsaExt
+        : IDsa
     {
         private bool			forSigning;
         private ECKeyParameters	key;
diff --git a/crypto/src/crypto/signers/Ed25519Signer.cs b/crypto/src/crypto/signers/Ed25519Signer.cs
index 4646ce1a5..59dc1bec5 100644
--- a/crypto/src/crypto/signers/Ed25519Signer.cs
+++ b/crypto/src/crypto/signers/Ed25519Signer.cs
@@ -52,6 +52,13 @@ namespace Org.BouncyCastle.Crypto.Signers
             buffer.Write(buf, off, len);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            buffer.Write(input);
+        }
+#endif
+
         public virtual byte[] GenerateSignature()
         {
             if (!forSigning || null == privateKey)
diff --git a/crypto/src/crypto/signers/Ed25519ctxSigner.cs b/crypto/src/crypto/signers/Ed25519ctxSigner.cs
index 293afedf5..4ccca8f22 100644
--- a/crypto/src/crypto/signers/Ed25519ctxSigner.cs
+++ b/crypto/src/crypto/signers/Ed25519ctxSigner.cs
@@ -55,6 +55,13 @@ namespace Org.BouncyCastle.Crypto.Signers
             buffer.Write(buf, off, len);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            buffer.Write(input);
+        }
+#endif
+
         public virtual byte[] GenerateSignature()
         {
             if (!forSigning || null == privateKey)
diff --git a/crypto/src/crypto/signers/Ed25519phSigner.cs b/crypto/src/crypto/signers/Ed25519phSigner.cs
index 8f4afab19..800447143 100644
--- a/crypto/src/crypto/signers/Ed25519phSigner.cs
+++ b/crypto/src/crypto/signers/Ed25519phSigner.cs
@@ -55,6 +55,13 @@ namespace Org.BouncyCastle.Crypto.Signers
             prehash.BlockUpdate(buf, off, len);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            prehash.BlockUpdate(input);
+        }
+#endif
+
         public virtual byte[] GenerateSignature()
         {
             if (!forSigning || null == privateKey)
diff --git a/crypto/src/crypto/signers/Ed448Signer.cs b/crypto/src/crypto/signers/Ed448Signer.cs
index 723ee7e33..3a7def690 100644
--- a/crypto/src/crypto/signers/Ed448Signer.cs
+++ b/crypto/src/crypto/signers/Ed448Signer.cs
@@ -55,6 +55,13 @@ namespace Org.BouncyCastle.Crypto.Signers
             buffer.Write(buf, off, len);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            buffer.Write(input);
+        }
+#endif
+
         public virtual byte[] GenerateSignature()
         {
             if (!forSigning || null == privateKey)
diff --git a/crypto/src/crypto/signers/Ed448phSigner.cs b/crypto/src/crypto/signers/Ed448phSigner.cs
index 197c2f706..30d4a0aba 100644
--- a/crypto/src/crypto/signers/Ed448phSigner.cs
+++ b/crypto/src/crypto/signers/Ed448phSigner.cs
@@ -55,6 +55,13 @@ namespace Org.BouncyCastle.Crypto.Signers
             prehash.BlockUpdate(buf, off, len);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            prehash.BlockUpdate(input);
+        }
+#endif
+
         public virtual byte[] GenerateSignature()
         {
             if (!forSigning || null == privateKey)
diff --git a/crypto/src/crypto/signers/GOST3410DigestSigner.cs b/crypto/src/crypto/signers/GOST3410DigestSigner.cs
index 81a5ff1cc..9564e43d3 100644
--- a/crypto/src/crypto/signers/GOST3410DigestSigner.cs
+++ b/crypto/src/crypto/signers/GOST3410DigestSigner.cs
@@ -15,11 +15,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         private int halfSize;
         private bool forSigning;
 
-
-
-        public Gost3410DigestSigner(
-            IDsa signer,
-            IDigest digest)
+        public Gost3410DigestSigner(IDsa signer, IDigest digest)
         {
             this.dsaSigner = signer;
             this.digest = digest;
@@ -34,9 +30,7 @@ namespace Org.BouncyCastle.Crypto.Signers
             get { return digest.AlgorithmName + "with" + dsaSigner.AlgorithmName; }
         }
 
-        public virtual void Init(
-            bool forSigning,
-            ICipherParameters parameters)
+        public virtual void Init(bool forSigning, ICipherParameters parameters)
         {
             this.forSigning = forSigning;
 
@@ -66,30 +60,23 @@ namespace Org.BouncyCastle.Crypto.Signers
             dsaSigner.Init(forSigning, parameters);
         }
 
-        /**
-		 * update the internal digest with the byte b
-		 */
-        public virtual void Update(
-            byte input)
+        public virtual void Update(byte input)
         {
             digest.Update(input);
         }
 
-        /**
-		 * update the internal digest with the byte array in
-		 */
-        public virtual void BlockUpdate(
-            byte[] input,
-            int inOff,
-            int length)
+        public virtual void BlockUpdate(byte[] input, int inOff, int inLen)
+        {
+            digest.BlockUpdate(input, inOff, inLen);
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
         {
-            digest.BlockUpdate(input, inOff, length);
+            digest.BlockUpdate(input);
         }
+#endif
 
-        /**
-		 * Generate a signature for the message we've been loaded with using
-		 * the key we were initialised with.
-		 */
         public virtual byte[] GenerateSignature()
         {
             if (!forSigning)
@@ -116,9 +103,7 @@ namespace Org.BouncyCastle.Crypto.Signers
             }
         }
 
-        /// <returns>true if the internal state represents the signature described in the passed in array.</returns>
-        public virtual bool VerifySignature(
-            byte[] signature)
+        public virtual bool VerifySignature(byte[] signature)
         {
             if (forSigning)
                 throw new InvalidOperationException("DSADigestSigner not initialised for verification");
@@ -140,7 +125,6 @@ namespace Org.BouncyCastle.Crypto.Signers
             return dsaSigner.VerifySignature(hash, R, S);
         }
 
-        /// <summary>Reset the internal state</summary>
         public virtual void Reset()
         {
             digest.Reset();
diff --git a/crypto/src/crypto/signers/GOST3410Signer.cs b/crypto/src/crypto/signers/GOST3410Signer.cs
index bcc1125b1..a0d8f8a1f 100644
--- a/crypto/src/crypto/signers/GOST3410Signer.cs
+++ b/crypto/src/crypto/signers/GOST3410Signer.cs
@@ -11,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 	 * Gost R 34.10-94 Signature Algorithm
 	 */
 	public class Gost3410Signer
-		: IDsaExt
+		: IDsa
 	{
 		private Gost3410KeyParameters key;
 		private SecureRandom random;
diff --git a/crypto/src/crypto/signers/GenericSigner.cs b/crypto/src/crypto/signers/GenericSigner.cs
index a5512176f..e0ff685ae 100644
--- a/crypto/src/crypto/signers/GenericSigner.cs
+++ b/crypto/src/crypto/signers/GenericSigner.cs
@@ -59,26 +59,23 @@ namespace Org.BouncyCastle.Crypto.Signers
             engine.Init(forSigning, parameters);
         }
 
-        /**
-        * update the internal digest with the byte b
-        */
         public virtual void Update(byte input)
         {
             digest.Update(input);
         }
 
-        /**
-        * update the internal digest with the byte array in
-        */
-        public virtual void BlockUpdate(byte[] input, int inOff, int length)
+        public virtual void BlockUpdate(byte[] input, int inOff, int inLen)
         {
-            digest.BlockUpdate(input, inOff, length);
+            digest.BlockUpdate(input, inOff, inLen);
         }
 
-        /**
-        * Generate a signature for the message we've been loaded with using the key
-        * we were initialised with.
-        */
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            digest.BlockUpdate(input);
+        }
+#endif
+
         public virtual byte[] GenerateSignature()
         {
             if (!forSigning)
@@ -90,10 +87,6 @@ namespace Org.BouncyCastle.Crypto.Signers
             return engine.ProcessBlock(hash, 0, hash.Length);
         }
 
-        /**
-        * return true if the internal state represents the signature described in
-        * the passed in array.
-        */
         public virtual bool VerifySignature(byte[] signature)
         {
             if (forSigning)
diff --git a/crypto/src/crypto/signers/Iso9796d2PssSigner.cs b/crypto/src/crypto/signers/Iso9796d2PssSigner.cs
index ad2718280..573765c1a 100644
--- a/crypto/src/crypto/signers/Iso9796d2PssSigner.cs
+++ b/crypto/src/crypto/signers/Iso9796d2PssSigner.cs
@@ -290,27 +290,46 @@ namespace Org.BouncyCastle.Crypto.Signers
             }
         }
 
-        /// <summary> update the internal digest with the byte array in</summary>
-        public virtual void BlockUpdate(
-            byte[]	input,
-            int		inOff,
-            int		length)
+        public virtual void BlockUpdate(byte[] input, int inOff, int inLen)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            BlockUpdate(input.AsSpan(inOff, inLen));
+#else
             if (preSig == null)
             {
-                while (length > 0 && messageLength < mBuf.Length)
+                while (inLen > 0 && messageLength < mBuf.Length)
                 {
                     this.Update(input[inOff]);
                     inOff++;
-                    length--;
+                    inLen--;
+                }
+            }
+
+            if (inLen > 0)
+            {
+                digest.BlockUpdate(input, inOff, inLen);
+            }
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            if (preSig == null)
+            {
+                while (!input.IsEmpty && messageLength < mBuf.Length)
+                {
+                    this.Update(input[0]);
+                    input = input[1..];
                 }
             }
 
-            if (length > 0)
+            if (!input.IsEmpty)
             {
-                digest.BlockUpdate(input, inOff, length);
+                digest.BlockUpdate(input);
             }
         }
+#endif
 
         /// <summary> reset the internal state</summary>
         public virtual void Reset()
diff --git a/crypto/src/crypto/signers/Iso9796d2Signer.cs b/crypto/src/crypto/signers/Iso9796d2Signer.cs
index f28c4ac71..ea1dc3f18 100644
--- a/crypto/src/crypto/signers/Iso9796d2Signer.cs
+++ b/crypto/src/crypto/signers/Iso9796d2Signer.cs
@@ -218,9 +218,7 @@ namespace Org.BouncyCastle.Crypto.Signers
             recoveredMessage.CopyTo(mBuf, 0);
         }
 
-        /// <summary> update the internal digest with the byte b</summary>
-        public virtual void Update(
-            byte input)
+        public virtual void Update(byte input)
         {
             digest.Update(input);
 
@@ -232,26 +230,42 @@ namespace Org.BouncyCastle.Crypto.Signers
             messageLength++;
         }
 
-        /// <summary> update the internal digest with the byte array in</summary>
-        public virtual void BlockUpdate(
-            byte[]	input,
-            int		inOff,
-            int		length)
+        public virtual void BlockUpdate(byte[] input, int inOff, int inLen)
         {
-            while (length > 0 && messageLength < mBuf.Length)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            BlockUpdate(input.AsSpan(inOff, inLen));
+#else
+            while (inLen > 0 && messageLength < mBuf.Length)
             {
-                //for (int i = 0; i < length && (i + messageLength) < mBuf.Length; i++)
-                //{
-                //    mBuf[messageLength + i] = input[inOff + i];
-                //}
                 this.Update(input[inOff]);
                 inOff++;
-                length--;
+                inLen--;
             }
 
-            digest.BlockUpdate(input, inOff, length);
-            messageLength += length;
+            if (inLen > 0)
+            {
+                digest.BlockUpdate(input, inOff, inLen);
+                messageLength += inLen;
+            }
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            while (!input.IsEmpty && messageLength < mBuf.Length)
+            {
+                this.Update(input[0]);
+                input = input[1..];
+            }
+
+            if (!input.IsEmpty)
+            {
+                digest.BlockUpdate(input);
+                messageLength += input.Length;
+            }
         }
+#endif
 
         /// <summary> reset the internal state</summary>
         public virtual void Reset()
diff --git a/crypto/src/crypto/signers/PssSigner.cs b/crypto/src/crypto/signers/PssSigner.cs
index 2a941df47..b033bb251 100644
--- a/crypto/src/crypto/signers/PssSigner.cs
+++ b/crypto/src/crypto/signers/PssSigner.cs
@@ -198,31 +198,28 @@ namespace Org.BouncyCastle.Crypto.Signers
 			Array.Clear(block, 0, block.Length);
 		}
 
-		/// <summary> update the internal digest with the byte b</summary>
-		public virtual void Update(
-			byte input)
+		public virtual void Update(byte input)
 		{
 			contentDigest1.Update(input);
 		}
 
-		/// <summary> update the internal digest with the byte array in</summary>
-		public virtual void BlockUpdate(
-			byte[]	input,
-			int		inOff,
-			int		length)
+		public virtual void BlockUpdate(byte[] input, int inOff, int inLen)
+		{
+			contentDigest1.BlockUpdate(input, inOff, inLen);
+		}
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public virtual void BlockUpdate(ReadOnlySpan<byte> input)
 		{
-			contentDigest1.BlockUpdate(input, inOff, length);
+			contentDigest1.BlockUpdate(input);
 		}
+#endif
 
-		/// <summary> reset the internal state</summary>
 		public virtual void Reset()
 		{
 			contentDigest1.Reset();
 		}
 
-		/// <summary> Generate a signature for the message we've been loaded with using
-		/// the key we were initialised with.
-		/// </summary>
 		public virtual byte[] GenerateSignature()
 		{
 			if (contentDigest1.GetDigestSize() != hLen)
@@ -268,11 +265,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 			return b;
 		}
 
-		/// <summary> return true if the internal state represents the signature described
-		/// in the passed in array.
-		/// </summary>
-		public virtual bool VerifySignature(
-			byte[] signature)
+		public virtual bool VerifySignature(byte[] signature)
 		{
 			if (contentDigest1.GetDigestSize() != hLen)
 				throw new InvalidOperationException();
diff --git a/crypto/src/crypto/signers/RsaDigestSigner.cs b/crypto/src/crypto/signers/RsaDigestSigner.cs
index 25bd4af4e..75b3a24b9 100644
--- a/crypto/src/crypto/signers/RsaDigestSigner.cs
+++ b/crypto/src/crypto/signers/RsaDigestSigner.cs
@@ -122,30 +122,23 @@ namespace Org.BouncyCastle.Crypto.Signers
             rsaEngine.Init(forSigning, parameters);
         }
 
-        /**
-         * update the internal digest with the byte b
-         */
-        public virtual void Update(
-            byte input)
+        public virtual void Update(byte input)
         {
             digest.Update(input);
         }
 
-        /**
-         * update the internal digest with the byte array in
-         */
-        public virtual void BlockUpdate(
-            byte[]	input,
-            int		inOff,
-            int		length)
+        public virtual void BlockUpdate(byte[] input, int inOff, int inLen)
         {
-            digest.BlockUpdate(input, inOff, length);
+            digest.BlockUpdate(input, inOff, inLen);
         }
 
-        /**
-         * Generate a signature for the message we've been loaded with using
-         * the key we were initialised with.
-         */
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            digest.BlockUpdate(input);
+        }
+#endif
+
         public virtual byte[] GenerateSignature()
         {
             if (!forSigning)
@@ -158,12 +151,7 @@ namespace Org.BouncyCastle.Crypto.Signers
             return rsaEngine.ProcessBlock(data, 0, data.Length);
         }
 
-        /**
-         * return true if the internal state represents the signature described
-         * in the passed in array.
-         */
-        public virtual bool VerifySignature(
-            byte[] signature)
+        public virtual bool VerifySignature(byte[] signature)
         {
             if (forSigning)
                 throw new InvalidOperationException("RsaDigestSigner not initialised for verification");
diff --git a/crypto/src/crypto/signers/SM2Signer.cs b/crypto/src/crypto/signers/SM2Signer.cs
index c344a220a..24aedd970 100644
--- a/crypto/src/crypto/signers/SM2Signer.cs
+++ b/crypto/src/crypto/signers/SM2Signer.cs
@@ -106,11 +106,18 @@ namespace Org.BouncyCastle.Crypto.Signers
             digest.Update(b);
         }
 
-        public virtual void BlockUpdate(byte[] buf, int off, int len)
+        public virtual void BlockUpdate(byte[] input, int inOff, int inLen)
         {
-            digest.BlockUpdate(buf, off, len);
+            digest.BlockUpdate(input, inOff, inLen);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            digest.BlockUpdate(input);
+        }
+#endif
+
         public virtual bool VerifySignature(byte[] signature)
         {
             try
diff --git a/crypto/src/crypto/signers/X931Signer.cs b/crypto/src/crypto/signers/X931Signer.cs
index 0907403a8..6c0aa9427 100644
--- a/crypto/src/crypto/signers/X931Signer.cs
+++ b/crypto/src/crypto/signers/X931Signer.cs
@@ -88,34 +88,28 @@ namespace Org.BouncyCastle.Crypto.Signers
             Array.Clear(block, 0, block.Length);
         }
 
-        /**
-         * update the internal digest with the byte b
-         */
         public virtual void Update(byte b)
         {
             digest.Update(b);
         }
 
-        /**
-         * update the internal digest with the byte array in
-         */
-        public virtual void BlockUpdate(byte[] input, int off, int len)
+        public virtual void BlockUpdate(byte[] input, int inOff, int inLen)
         {
-            digest.BlockUpdate(input, off, len);
+            digest.BlockUpdate(input, inOff, inLen);
         }
 
-        /**
-         * reset the internal state
-         */
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            digest.BlockUpdate(input);
+        }
+#endif
+
         public virtual void Reset()
         {
             digest.Reset();
         }
 
-        /**
-         * generate a signature for the loaded message using the key we were
-         * initialised with.
-         */
         public virtual byte[] GenerateSignature()
         {
             CreateSignatureBlock();
@@ -156,10 +150,6 @@ namespace Org.BouncyCastle.Crypto.Signers
             block[delta - 1] = (byte)0xba;
         }
 
-        /**
-         * return true if the signature represents a ISO9796-2 signature
-         * for the passed in message.
-         */
         public virtual bool VerifySignature(byte[] signature)
         {
             try
diff --git a/crypto/src/crypto/util/Pack.cs b/crypto/src/crypto/util/Pack.cs
index fd6ee1d9f..7706b5a42 100644
--- a/crypto/src/crypto/util/Pack.cs
+++ b/crypto/src/crypto/util/Pack.cs
@@ -1,4 +1,5 @@
 using System;
+using System.Runtime.CompilerServices;
 
 namespace Org.BouncyCastle.Crypto.Utilities
 {
@@ -441,6 +442,7 @@ namespace Org.BouncyCastle.Crypto.Utilities
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static uint BE_To_UInt32(ReadOnlySpan<byte> bs)
         {
             return (uint)bs[0] << 24
@@ -449,6 +451,7 @@ namespace Org.BouncyCastle.Crypto.Utilities
                 |        bs[3];
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static void BE_To_UInt32(ReadOnlySpan<byte> bs, Span<uint> ns)
         {
             for (int i = 0; i < ns.Length; ++i)
@@ -458,6 +461,7 @@ namespace Org.BouncyCastle.Crypto.Utilities
             }
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static ulong BE_To_UInt64(ReadOnlySpan<byte> bs)
         {
             uint hi = BE_To_UInt32(bs);
@@ -465,6 +469,7 @@ namespace Org.BouncyCastle.Crypto.Utilities
             return ((ulong)hi << 32) | lo;
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static void BE_To_UInt64(ReadOnlySpan<byte> bs, Span<ulong> ns)
         {
             for (int i = 0; i < ns.Length; ++i)
@@ -474,6 +479,7 @@ namespace Org.BouncyCastle.Crypto.Utilities
             }
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static uint LE_To_UInt32(ReadOnlySpan<byte> bs)
         {
             return      bs[0]
@@ -482,6 +488,7 @@ namespace Org.BouncyCastle.Crypto.Utilities
                 | (uint)bs[3] << 24;
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static void LE_To_UInt32(ReadOnlySpan<byte> bs, Span<uint> ns)
         {
             for (int i = 0; i < ns.Length; ++i)
@@ -491,6 +498,7 @@ namespace Org.BouncyCastle.Crypto.Utilities
             }
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static ulong LE_To_UInt64(ReadOnlySpan<byte> bs)
         {
             uint lo = LE_To_UInt32(bs);
@@ -498,6 +506,7 @@ namespace Org.BouncyCastle.Crypto.Utilities
             return (ulong)hi << 32 | lo;
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static void LE_To_UInt64(ReadOnlySpan<byte> bs, Span<ulong> ns)
         {
             for (int i = 0; i < ns.Length; ++i)
@@ -507,6 +516,7 @@ namespace Org.BouncyCastle.Crypto.Utilities
             }
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static void UInt32_To_BE(uint n, Span<byte> bs)
         {
             bs[0] = (byte)(n >> 24);
@@ -515,6 +525,7 @@ namespace Org.BouncyCastle.Crypto.Utilities
             bs[3] = (byte) n;
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static void UInt32_To_BE(ReadOnlySpan<uint> ns, Span<byte> bs)
         {
             for (int i = 0; i < ns.Length; ++i)
@@ -524,6 +535,7 @@ namespace Org.BouncyCastle.Crypto.Utilities
             }
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static void UInt32_To_LE(uint n, Span<byte> bs)
         {
             bs[0] = (byte) n;
@@ -532,6 +544,7 @@ namespace Org.BouncyCastle.Crypto.Utilities
             bs[3] = (byte)(n >> 24);
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static void UInt32_To_LE(ReadOnlySpan<uint> ns, Span<byte> bs)
         {
             for (int i = 0; i < ns.Length; ++i)
@@ -541,12 +554,14 @@ namespace Org.BouncyCastle.Crypto.Utilities
             }
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static void UInt64_To_BE(ulong n, Span<byte> bs)
         {
             UInt32_To_BE((uint)(n >> 32), bs);
             UInt32_To_BE((uint)n, bs[4..]);
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static void UInt64_To_BE(ReadOnlySpan<ulong> ns, Span<byte> bs)
         {
             for (int i = 0; i < ns.Length; ++i)
@@ -556,12 +571,14 @@ namespace Org.BouncyCastle.Crypto.Utilities
             }
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static void UInt64_To_LE(ulong n, Span<byte> bs)
         {
             UInt32_To_LE((uint)n, bs);
             UInt32_To_LE((uint)(n >> 32), bs[4..]);
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static void UInt64_To_LE(ReadOnlySpan<ulong> ns, Span<byte> bs)
         {
             for (int i = 0; i < ns.Length; ++i)
diff --git a/crypto/src/pqc/crypto/lms/LMSContext.cs b/crypto/src/pqc/crypto/lms/LMSContext.cs
index 35c33093b..2113184fe 100644
--- a/crypto/src/pqc/crypto/lms/LMSContext.cs
+++ b/crypto/src/pqc/crypto/lms/LMSContext.cs
@@ -124,5 +124,17 @@ namespace Org.BouncyCastle.Pqc.Crypto.Lms
         {
             digest.Reset();
         }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            digest.BlockUpdate(input);
+        }
+
+        public int DoFinal(Span<byte> output)
+        {
+            return digest.DoFinal(output);
+        }
+#endif
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/src/pqc/crypto/sphincsplus/Adrs.cs b/crypto/src/pqc/crypto/sphincsplus/Adrs.cs
index a6ec753fd..229fe8133 100644
--- a/crypto/src/pqc/crypto/sphincsplus/Adrs.cs
+++ b/crypto/src/pqc/crypto/sphincsplus/Adrs.cs
@@ -1,11 +1,10 @@
-
 using System;
+
 using Org.BouncyCastle.Crypto.Utilities;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
 {
-
     internal class Adrs
     {
         public static uint WOTS_HASH = 0;
diff --git a/crypto/src/pqc/crypto/sphincsplus/HarakaS256Digest.cs b/crypto/src/pqc/crypto/sphincsplus/HarakaS256Digest.cs
index 1e2ef67cf..65f75b068 100644
--- a/crypto/src/pqc/crypto/sphincsplus/HarakaS256Digest.cs
+++ b/crypto/src/pqc/crypto/sphincsplus/HarakaS256Digest.cs
@@ -1,7 +1,9 @@
 using System;
-namespace Org.BouncyCastle.pqc.crypto.sphincsplus
+
+namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
 {
-    class HarakaS256Digest : HarakaSBase
+    internal class HarakaS256Digest
+        : HarakaSBase
     {
         public HarakaS256Digest(HarakaSXof harakaSXof)
         {
diff --git a/crypto/src/pqc/crypto/sphincsplus/HarakaS512Digest.cs b/crypto/src/pqc/crypto/sphincsplus/HarakaS512Digest.cs
index 883d731be..a1c1e3db9 100644
--- a/crypto/src/pqc/crypto/sphincsplus/HarakaS512Digest.cs
+++ b/crypto/src/pqc/crypto/sphincsplus/HarakaS512Digest.cs
@@ -1,5 +1,6 @@
 using System;
-namespace Org.BouncyCastle.pqc.crypto.sphincsplus
+
+namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
 {
     /**
     * Haraka-512 v2, https://eprint.iacr.org/2016/098.pdf
@@ -7,7 +8,8 @@ namespace Org.BouncyCastle.pqc.crypto.sphincsplus
     * Haraka512-256 with reference to Python Reference Impl from: https://github.com/sphincs/sphincsplus
     * </p>
     */
-    class HarakaS512Digest : HarakaSBase
+    internal class HarakaS512Digest
+        : HarakaSBase
     {
         public HarakaS512Digest(HarakaSBase harakaSBase)
         {
@@ -43,7 +45,6 @@ namespace Org.BouncyCastle.pqc.crypto.sphincsplus
             off += len;
         }
 
-
         public int DoFinal(byte[] output, int outOff)
         {
             byte[] s = new byte[64];
diff --git a/crypto/src/pqc/crypto/sphincsplus/HarakaSBase.cs b/crypto/src/pqc/crypto/sphincsplus/HarakaSBase.cs
index c10e2e195..8af826dba 100644
--- a/crypto/src/pqc/crypto/sphincsplus/HarakaSBase.cs
+++ b/crypto/src/pqc/crypto/sphincsplus/HarakaSBase.cs
@@ -1,7 +1,8 @@
 using System;
+
 using Org.BouncyCastle.Utilities;
 
-namespace Org.BouncyCastle.pqc.crypto.sphincsplus
+namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
 {
     /**
      * Haraka-512 v2, https://eprint.iacr.org/2016/098.pdf
@@ -9,19 +10,19 @@ namespace Org.BouncyCastle.pqc.crypto.sphincsplus
      * Haraka512-256 with reference to Python Reference Impl from: https://github.com/sphincs/sphincsplus
      * </p>
      */
-    public class HarakaSBase
+    internal class HarakaSBase
     {
         internal ulong[][] haraka512_rc = new ulong[][]{
-        new ulong[]{0x24cf0ab9086f628bL, 0xbdd6eeecc83b8382L, 0xd96fb0306cdad0a7L, 0xaace082ac8f95f89L, 0x449d8e8870d7041fL, 0x49bb2f80b2b3e2f8L, 0x0569ae98d93bb258L, 0x23dc9691e7d6a4b1L},
-        new ulong[]{0xd8ba10ede0fe5b6eL, 0x7ecf7dbe424c7b8eL, 0x6ea9949c6df62a31L, 0xbf3f3c97ec9c313eL, 0x241d03a196a1861eL, 0xead3a51116e5a2eaL, 0x77d479fcad9574e3L, 0x18657a1af894b7a0L},
-        new ulong[]{0x10671e1a7f595522L, 0xd9a00ff675d28c7bL, 0x2f1edf0d2b9ba661L, 0xb8ff58b8e3de45f9L, 0xee29261da9865c02L, 0xd1532aa4b50bdf43L, 0x8bf858159b231bb1L, 0xdf17439d22d4f599L},
-        new ulong[]{0xdd4b2f0870b918c0L, 0x757a81f3b39b1bb6L, 0x7a5c556898952e3fL, 0x7dd70a16d915d87aL, 0x3ae61971982b8301L, 0xc3ab319e030412beL, 0x17c0033ac094a8cbL, 0x5a0630fc1a8dc4efL},
-        new ulong[]{0x17708988c1632f73L, 0xf92ddae090b44f4fL, 0x11ac0285c43aa314L, 0x509059941936b8baL, 0xd03e152fa2ce9b69L, 0x3fbcbcb63a32998bL, 0x6204696d692254f7L, 0x915542ed93ec59b4L},
-        new ulong[]{0xf4ed94aa8879236eL, 0xff6cb41cd38e03c0L, 0x069b38602368aeabL, 0x669495b820f0ddbaL, 0xf42013b1b8bf9e3dL, 0xcf935efe6439734dL, 0xbc1dcf42ca29e3f8L, 0x7e6d3ed29f78ad67L},
-        new ulong[]{0xf3b0f6837ffcddaaL, 0x3a76faef934ddf41L, 0xcec7ae583a9c8e35L, 0xe4dd18c68f0260afL, 0x2c0e5df1ad398eaaL, 0x478df5236ae22e8cL, 0xfb944c46fe865f39L, 0xaa48f82f028132baL},
-        new ulong[]{0x231b9ae2b76aca77L, 0x292a76a712db0b40L, 0x5850625dc8134491L, 0x73137dd469810fb5L, 0x8a12a6a202a474fdL, 0xd36fd9daa78bdb80L, 0xb34c5e733505706fL, 0xbaf1cdca818d9d96L},
-        new ulong[]{0x2e99781335e8c641L, 0xbddfe5cce47d560eL, 0xf74e9bf32e5e040cL, 0x1d7a709d65996be9L, 0x670df36a9cf66cddL, 0xd05ef84a176a2875L, 0x0f888e828cb1c44eL, 0x1a79e9c9727b052cL},
-        new ulong[]{0x83497348628d84deL, 0x2e9387d51f22a754L, 0xb000068da2f852d6L, 0x378c9e1190fd6fe5L, 0x870027c316de7293L, 0xe51a9d4462e047bbL, 0x90ecf7f8c6251195L, 0x655953bfbed90a9cL},
+            new ulong[]{0x24cf0ab9086f628bL, 0xbdd6eeecc83b8382L, 0xd96fb0306cdad0a7L, 0xaace082ac8f95f89L, 0x449d8e8870d7041fL, 0x49bb2f80b2b3e2f8L, 0x0569ae98d93bb258L, 0x23dc9691e7d6a4b1L},
+            new ulong[]{0xd8ba10ede0fe5b6eL, 0x7ecf7dbe424c7b8eL, 0x6ea9949c6df62a31L, 0xbf3f3c97ec9c313eL, 0x241d03a196a1861eL, 0xead3a51116e5a2eaL, 0x77d479fcad9574e3L, 0x18657a1af894b7a0L},
+            new ulong[]{0x10671e1a7f595522L, 0xd9a00ff675d28c7bL, 0x2f1edf0d2b9ba661L, 0xb8ff58b8e3de45f9L, 0xee29261da9865c02L, 0xd1532aa4b50bdf43L, 0x8bf858159b231bb1L, 0xdf17439d22d4f599L},
+            new ulong[]{0xdd4b2f0870b918c0L, 0x757a81f3b39b1bb6L, 0x7a5c556898952e3fL, 0x7dd70a16d915d87aL, 0x3ae61971982b8301L, 0xc3ab319e030412beL, 0x17c0033ac094a8cbL, 0x5a0630fc1a8dc4efL},
+            new ulong[]{0x17708988c1632f73L, 0xf92ddae090b44f4fL, 0x11ac0285c43aa314L, 0x509059941936b8baL, 0xd03e152fa2ce9b69L, 0x3fbcbcb63a32998bL, 0x6204696d692254f7L, 0x915542ed93ec59b4L},
+            new ulong[]{0xf4ed94aa8879236eL, 0xff6cb41cd38e03c0L, 0x069b38602368aeabL, 0x669495b820f0ddbaL, 0xf42013b1b8bf9e3dL, 0xcf935efe6439734dL, 0xbc1dcf42ca29e3f8L, 0x7e6d3ed29f78ad67L},
+            new ulong[]{0xf3b0f6837ffcddaaL, 0x3a76faef934ddf41L, 0xcec7ae583a9c8e35L, 0xe4dd18c68f0260afL, 0x2c0e5df1ad398eaaL, 0x478df5236ae22e8cL, 0xfb944c46fe865f39L, 0xaa48f82f028132baL},
+            new ulong[]{0x231b9ae2b76aca77L, 0x292a76a712db0b40L, 0x5850625dc8134491L, 0x73137dd469810fb5L, 0x8a12a6a202a474fdL, 0xd36fd9daa78bdb80L, 0xb34c5e733505706fL, 0xbaf1cdca818d9d96L},
+            new ulong[]{0x2e99781335e8c641L, 0xbddfe5cce47d560eL, 0xf74e9bf32e5e040cL, 0x1d7a709d65996be9L, 0x670df36a9cf66cddL, 0xd05ef84a176a2875L, 0x0f888e828cb1c44eL, 0x1a79e9c9727b052cL},
+            new ulong[]{0x83497348628d84deL, 0x2e9387d51f22a754L, 0xb000068da2f852d6L, 0x378c9e1190fd6fe5L, 0x870027c316de7293L, 0xe51a9d4462e047bbL, 0x90ecf7f8c6251195L, 0x655953bfbed90a9cL},
         };
 
         internal uint[][] haraka256_rc = new uint[10][];
@@ -791,4 +792,3 @@ namespace Org.BouncyCastle.pqc.crypto.sphincsplus
         }
     }
 }
-
diff --git a/crypto/src/pqc/crypto/sphincsplus/HarakaSXof.cs b/crypto/src/pqc/crypto/sphincsplus/HarakaSXof.cs
index bcd62aa87..97bd3c07c 100644
--- a/crypto/src/pqc/crypto/sphincsplus/HarakaSXof.cs
+++ b/crypto/src/pqc/crypto/sphincsplus/HarakaSXof.cs
@@ -1,7 +1,9 @@
 using System;
-namespace Org.BouncyCastle.pqc.crypto.sphincsplus
+
+namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
 {
-    class HarakaSXof : HarakaSBase
+    internal class HarakaSXof
+        : HarakaSBase
     {
         public String GetAlgorithmName()
         {
@@ -82,6 +84,4 @@ namespace Org.BouncyCastle.pqc.crypto.sphincsplus
             return outLen;
         }
     }
-
 }
-
diff --git a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs
index f93d43183..e7455d253 100644
--- a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs
+++ b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs
@@ -6,12 +6,11 @@ using Org.BouncyCastle.Crypto.Generators;
 using Org.BouncyCastle.Crypto.Macs;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Crypto.Utilities;
-using Org.BouncyCastle.pqc.crypto.sphincsplus;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
 {
-    abstract class SPHINCSPlusEngine
+    internal abstract class SPHINCSPlusEngine
     {
         bool robust;
 
@@ -91,7 +90,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
             this.T = 1U << a;
         }
 
-        public abstract void init(byte[] pkSeed);
+        public abstract void Init(byte[] pkSeed);
 
         public abstract byte[] F(byte[] pkSeed, Adrs adrs, byte[] m1);
 
@@ -146,7 +145,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
                 this.msgDigestBuf = new byte[msgDigest.GetDigestSize()];
             }
 
-            public override void init(byte[] pkSeed)
+            public override void Init(byte[] pkSeed)
             {
                 byte[] padding = new byte[bl];
 
@@ -366,7 +365,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
                 this.maskDigest = new ShakeDigest(256);
             }
 
-            public override void init(byte[] pkSeed)
+            public override void Init(byte[] pkSeed)
             {
                 // TODO: add use of memo
             }
@@ -526,7 +525,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
 
         }
 
-        internal class HarakaSEngine : SPHINCSPlusEngine
+        internal class HarakaSEngine
+            : SPHINCSPlusEngine
         {
             public HarakaSXof harakaSXof;
             public HarakaS256Digest harakaS256Digest;
@@ -537,7 +537,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
 
             }
 
-            public override void init(byte[] pkSeed)
+            public override void Init(byte[] pkSeed)
             {
                 harakaSXof = new HarakaSXof(pkSeed);
                 harakaS256Digest = new HarakaS256Digest(harakaSXof);
diff --git a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusKeyPairGenerator.cs b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusKeyPairGenerator.cs
index dbb93a812..0eaf9557a 100644
--- a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusKeyPairGenerator.cs
+++ b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusKeyPairGenerator.cs
@@ -41,7 +41,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
                 sk = new SK(SecRand(engine.N), SecRand(engine.N));
                 pkSeed = SecRand(engine.N);
             }
-            engine.init(pkSeed);
+            engine.Init(pkSeed);
             // TODO
             PK pk = new PK(pkSeed, new HT(engine, sk.seed, pkSeed).HTPubKey);
 
@@ -51,11 +51,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
 
         private byte[] SecRand(int n)
         {
-            byte[] rv = new byte[n];
-
-            random.NextBytes(rv);
-
-            return rv;
+            return SecureRandom.GetNextBytes(random, n);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusSigner.cs b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusSigner.cs
index 1a1c90766..c6664f889 100644
--- a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusSigner.cs
+++ b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusSigner.cs
@@ -60,7 +60,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
             // init
 
             SPHINCSPlusEngine engine = privKey.GetParameters().GetEngine();
-            engine.init(privKey.GetPublicSeed());
+            engine.Init(privKey.GetPublicSeed());
             // generate randomizer
             byte[] optRand = new byte[engine.N];
             if (random != null)
@@ -119,7 +119,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus
 
             // init
             SPHINCSPlusEngine engine = pubKey.GetParameters().GetEngine();
-            engine.init(pubKey.GetSeed());
+            engine.Init(pubKey.GetSeed());
 
             Adrs adrs = new Adrs();
             SIG sig = new SIG(engine.N, engine.K, engine.A, engine.D, engine.H_PRIME, engine.WOTS_LEN, signature);
diff --git a/crypto/src/security/DigestUtilities.cs b/crypto/src/security/DigestUtilities.cs
index 2c9e89277..035280fd6 100644
--- a/crypto/src/security/DigestUtilities.cs
+++ b/crypto/src/security/DigestUtilities.cs
@@ -266,9 +266,22 @@ namespace Org.BouncyCastle.Security
         public static byte[] CalculateDigest(string algorithm, byte[] input)
         {
             IDigest digest = GetDigest(algorithm);
-            digest.BlockUpdate(input, 0, input.Length);
-            return DoFinal(digest);
+            return DoFinal(digest, input);
+        }
+
+        public static byte[] CalculateDigest(string algorithm, byte[] buf, int off, int len)
+        {
+            IDigest digest = GetDigest(algorithm);
+            return DoFinal(digest, buf, off, len);
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public static byte[] CalculateDigest(string algorithm, ReadOnlySpan<byte> buffer)
+        {
+            IDigest digest = GetDigest(algorithm);
+            return DoFinal(digest, buffer);
         }
+#endif
 
         public static byte[] DoFinal(IDigest digest)
         {
@@ -282,5 +295,19 @@ namespace Org.BouncyCastle.Security
             digest.BlockUpdate(input, 0, input.Length);
             return DoFinal(digest);
         }
+
+        public static byte[] DoFinal(IDigest digest, byte[] buf, int off, int len)
+        {
+            digest.BlockUpdate(buf, off, len);
+            return DoFinal(digest);
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public static byte[] DoFinal(IDigest digest, ReadOnlySpan<byte> buffer)
+        {
+            digest.BlockUpdate(buffer);
+            return DoFinal(digest);
+        }
+#endif
     }
 }
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs b/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs
index ae05a1664..1667e5d6f 100644
--- a/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs
@@ -10,11 +10,11 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
         : TlsAeadCipherImpl
     {
         private readonly bool m_isEncrypting;
-        private readonly IAeadBlockCipher m_cipher;
+        private readonly IAeadCipher m_cipher;
 
         private KeyParameter key;
 
-        internal BcTlsAeadCipherImpl(IAeadBlockCipher cipher, bool isEncrypting)
+        internal BcTlsAeadCipherImpl(IAeadCipher cipher, bool isEncrypting)
         {
             this.m_cipher = cipher;
             this.m_isEncrypting = isEncrypting;
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs b/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs
index 0375950c3..e84361e49 100644
--- a/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs
@@ -499,8 +499,8 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
         protected virtual TlsAeadCipher CreateCipher_Aes_Ccm(TlsCryptoParameters cryptoParams, int cipherKeySize,
             int macSize)
         {
-            BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Aes_Ccm(), true);
-            BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Aes_Ccm(), false);
+            BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_Aes_Ccm(), true);
+            BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_Aes_Ccm(), false);
 
             return new TlsAeadCipher(cryptoParams, encrypt, decrypt, cipherKeySize, macSize, TlsAeadCipher.AEAD_CCM);
         }
@@ -508,8 +508,8 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
         protected virtual TlsAeadCipher CreateCipher_Aes_Gcm(TlsCryptoParameters cryptoParams, int cipherKeySize,
             int macSize)
         {
-            BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Aes_Gcm(), true);
-            BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Aes_Gcm(), false);
+            BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_Aes_Gcm(), true);
+            BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_Aes_Gcm(), false);
 
             return new TlsAeadCipher(cryptoParams, encrypt, decrypt, cipherKeySize, macSize, TlsAeadCipher.AEAD_GCM);
         }
@@ -517,8 +517,8 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
         protected virtual TlsAeadCipher CreateCipher_Aria_Gcm(TlsCryptoParameters cryptoParams, int cipherKeySize,
             int macSize)
         {
-            BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Aria_Gcm(), true);
-            BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Aria_Gcm(), false);
+            BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_Aria_Gcm(), true);
+            BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_Aria_Gcm(), false);
 
             return new TlsAeadCipher(cryptoParams, encrypt, decrypt, cipherKeySize, macSize, TlsAeadCipher.AEAD_GCM);
         }
@@ -526,8 +526,8 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
         protected virtual TlsAeadCipher CreateCipher_Camellia_Gcm(TlsCryptoParameters cryptoParams, int cipherKeySize,
             int macSize)
         {
-            BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Camellia_Gcm(), true);
-            BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_Camellia_Gcm(), false);
+            BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_Camellia_Gcm(), true);
+            BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_Camellia_Gcm(), false);
 
             return new TlsAeadCipher(cryptoParams, encrypt, decrypt, cipherKeySize, macSize, TlsAeadCipher.AEAD_GCM);
         }
@@ -546,16 +546,16 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
 
         protected virtual TlsAeadCipher CreateCipher_SM4_Ccm(TlsCryptoParameters cryptoParams)
         {
-            BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_SM4_Ccm(), true);
-            BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_SM4_Ccm(), false);
+            BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_SM4_Ccm(), true);
+            BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_SM4_Ccm(), false);
 
             return new TlsAeadCipher(cryptoParams, encrypt, decrypt, 16, 16, TlsAeadCipher.AEAD_CCM);
         }
 
         protected virtual TlsAeadCipher CreateCipher_SM4_Gcm(TlsCryptoParameters cryptoParams)
         {
-            BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_SM4_Gcm(), true);
-            BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadBlockCipher_SM4_Gcm(), false);
+            BcTlsAeadCipherImpl encrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_SM4_Gcm(), true);
+            BcTlsAeadCipherImpl decrypt = new BcTlsAeadCipherImpl(CreateAeadCipher_SM4_Gcm(), false);
 
             return new TlsAeadCipher(cryptoParams, encrypt, decrypt, 16, 16, TlsAeadCipher.AEAD_GCM);
         }
@@ -596,43 +596,43 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             return new SM4Engine();
         }
 
-        protected virtual IAeadBlockCipher CreateCcmMode(IBlockCipher engine)
+        protected virtual IAeadCipher CreateCcmMode(IBlockCipher engine)
         {
             return new CcmBlockCipher(engine);
         }
 
-        protected virtual IAeadBlockCipher CreateGcmMode(IBlockCipher engine)
+        protected virtual IAeadCipher CreateGcmMode(IBlockCipher engine)
         {
             // TODO Consider allowing custom configuration of multiplier
             return new GcmBlockCipher(engine);
         }
 
-        protected virtual IAeadBlockCipher CreateAeadBlockCipher_Aes_Ccm()
+        protected virtual IAeadCipher CreateAeadCipher_Aes_Ccm()
         {
             return CreateCcmMode(CreateAesEngine());
         }
 
-        protected virtual IAeadBlockCipher CreateAeadBlockCipher_Aes_Gcm()
+        protected virtual IAeadCipher CreateAeadCipher_Aes_Gcm()
         {
             return CreateGcmMode(CreateAesEngine());
         }
 
-        protected virtual IAeadBlockCipher CreateAeadBlockCipher_Aria_Gcm()
+        protected virtual IAeadCipher CreateAeadCipher_Aria_Gcm()
         {
             return CreateGcmMode(CreateAriaEngine());
         }
 
-        protected virtual IAeadBlockCipher CreateAeadBlockCipher_Camellia_Gcm()
+        protected virtual IAeadCipher CreateAeadCipher_Camellia_Gcm()
         {
             return CreateGcmMode(CreateCamelliaEngine());
         }
 
-        protected virtual IAeadBlockCipher CreateAeadBlockCipher_SM4_Ccm()
+        protected virtual IAeadCipher CreateAeadCipher_SM4_Ccm()
         {
             return CreateCcmMode(CreateSM4Engine());
         }
 
-        protected virtual IAeadBlockCipher CreateAeadBlockCipher_SM4_Gcm()
+        protected virtual IAeadCipher CreateAeadCipher_SM4_Gcm()
         {
             return CreateGcmMode(CreateSM4Engine());
         }
diff --git a/crypto/src/util/Integers.cs b/crypto/src/util/Integers.cs
index ff907ac80..b0c899500 100644
--- a/crypto/src/util/Integers.cs
+++ b/crypto/src/util/Integers.cs
@@ -1,5 +1,6 @@
 using System;
 #if NETCOREAPP3_0_OR_GREATER
+using System.Numerics;
 using System.Runtime.Intrinsics.X86;
 #endif
 
@@ -107,24 +108,40 @@ namespace Org.BouncyCastle.Utilities
 
         public static int RotateLeft(int i, int distance)
         {
+#if NETCOREAPP3_0_OR_GREATER
+            return (int)BitOperations.RotateLeft((uint)i, distance);
+#else
             return (i << distance) ^ (int)((uint)i >> -distance);
+#endif
         }
 
         [CLSCompliant(false)]
         public static uint RotateLeft(uint i, int distance)
         {
+#if NETCOREAPP3_0_OR_GREATER
+            return BitOperations.RotateLeft(i, distance);
+#else
             return (i << distance) ^ (i >> -distance);
+#endif
         }
 
         public static int RotateRight(int i, int distance)
         {
+#if NETCOREAPP3_0_OR_GREATER
+            return (int)BitOperations.RotateRight((uint)i, distance);
+#else
             return (int)((uint)i >> distance) ^ (i << -distance);
+#endif
         }
 
         [CLSCompliant(false)]
         public static uint RotateRight(uint i, int distance)
         {
+#if NETCOREAPP3_0_OR_GREATER
+            return BitOperations.RotateRight(i, distance);
+#else
             return (i >> distance) ^ (i << -distance);
+#endif
         }
     }
 }
diff --git a/crypto/src/util/Longs.cs b/crypto/src/util/Longs.cs
index 45dd91090..0bb35de25 100644
--- a/crypto/src/util/Longs.cs
+++ b/crypto/src/util/Longs.cs
@@ -1,5 +1,6 @@
 using System;
 #if NETCOREAPP3_0_OR_GREATER
+using System.Numerics;
 using System.Runtime.Intrinsics.X86;
 #endif
 
@@ -108,24 +109,40 @@ namespace Org.BouncyCastle.Utilities
 
         public static long RotateLeft(long i, int distance)
         {
+#if NETCOREAPP3_0_OR_GREATER
+            return (long)BitOperations.RotateLeft((ulong)i, distance);
+#else
             return (i << distance) ^ (long)((ulong)i >> -distance);
+#endif
         }
 
         [CLSCompliant(false)]
         public static ulong RotateLeft(ulong i, int distance)
         {
+#if NETCOREAPP3_0_OR_GREATER
+            return BitOperations.RotateLeft(i, distance);
+#else
             return (i << distance) ^ (i >> -distance);
+#endif
         }
 
         public static long RotateRight(long i, int distance)
         {
+#if NETCOREAPP3_0_OR_GREATER
+            return (long)BitOperations.RotateRight((ulong)i, distance);
+#else
             return (long)((ulong)i >> distance) ^ (i << -distance);
+#endif
         }
 
         [CLSCompliant(false)]
         public static ulong RotateRight(ulong i, int distance)
         {
+#if NETCOREAPP3_0_OR_GREATER
+            return BitOperations.RotateRight(i, distance);
+#else
             return (i >> distance) ^ (i << -distance);
+#endif
         }
     }
 }
diff --git a/crypto/test/BouncyCastle.Crypto.Tests.csproj b/crypto/test/BouncyCastle.Crypto.Tests.csproj
index 43f348fa8..8ae0914d5 100644
--- a/crypto/test/BouncyCastle.Crypto.Tests.csproj
+++ b/crypto/test/BouncyCastle.Crypto.Tests.csproj
@@ -22,7 +22,7 @@
     <EmbeddedResource Include="data\**\*.*" Exclude="**\README.txt" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.0" />
     <PackageReference Include="NUnit" Version="3.13.3" />
     <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
   </ItemGroup>
diff --git a/crypto/test/src/crypto/prng/test/CtrDrbgTest.cs b/crypto/test/src/crypto/prng/test/CtrDrbgTest.cs
index 65209abdb..3e90c5752 100644
--- a/crypto/test/src/crypto/prng/test/CtrDrbgTest.cs
+++ b/crypto/test/src/crypto/prng/test/CtrDrbgTest.cs
@@ -512,6 +512,15 @@ namespace Org.BouncyCastle.Crypto.Prng.Test
                 return cipher.ProcessBlock(input, inOff, output, outOff);
             }
 
+            // NOTE: .NET Core 2.1 has Span<T>, but is tested against our .NET Standard 2.0 assembly.
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+            {
+                return cipher.ProcessBlock(input, output);
+            }
+#endif
+
             public void Reset()
             {
                 cipher.Reset();
diff --git a/crypto/test/src/crypto/test/Blake2bDigestTest.cs b/crypto/test/src/crypto/test/Blake2bDigestTest.cs
index e7835e2a7..518331b2e 100644
--- a/crypto/test/src/crypto/test/Blake2bDigestTest.cs
+++ b/crypto/test/src/crypto/test/Blake2bDigestTest.cs
@@ -146,6 +146,8 @@ namespace Org.BouncyCastle.Crypto.Tests
             ResetTest();
             DoTestNullKeyVsUnkeyed();
             DoTestLengthConstruction();
+
+            DigestTest.SpanConsistencyTests(this, new Blake2bDigest(512));
         }
 
         private void CloneTest()
diff --git a/crypto/test/src/crypto/test/Blake2sDigestTest.cs b/crypto/test/src/crypto/test/Blake2sDigestTest.cs
index 2080e2871..10c35579f 100644
--- a/crypto/test/src/crypto/test/Blake2sDigestTest.cs
+++ b/crypto/test/src/crypto/test/Blake2sDigestTest.cs
@@ -293,6 +293,8 @@ namespace Org.BouncyCastle.Crypto.Tests
             RunSelfTest();
             DoTestNullKeyVsUnkeyed();
             DoTestLengthConstruction();
+
+            DigestTest.SpanConsistencyTests(this, new Blake2sDigest(256));
         }
 
         [Test]
diff --git a/crypto/test/src/crypto/test/CSHAKETest.cs b/crypto/test/src/crypto/test/CSHAKETest.cs
index 6a3c99a0f..581832aaf 100644
--- a/crypto/test/src/crypto/test/CSHAKETest.cs
+++ b/crypto/test/src/crypto/test/CSHAKETest.cs
@@ -103,6 +103,9 @@ namespace Org.BouncyCastle.Crypto.Tests
             checkSHAKE(128, new CShakeDigest(128, null, new byte[0]), Hex.Decode("eeaabeef"));
             checkSHAKE(128, new CShakeDigest(128, null, null), Hex.Decode("eeaabeef"));
             checkSHAKE(256, new CShakeDigest(256, null, null), Hex.Decode("eeaabeef"));
+
+            DigestTest.SpanConsistencyTests(this, new CShakeDigest(128, null, null));
+            DigestTest.SpanConsistencyTests(this, new CShakeDigest(256, null, null));
         }
 
         private void checkZeroPadZ()
diff --git a/crypto/test/src/crypto/test/DigestTest.cs b/crypto/test/src/crypto/test/DigestTest.cs
index 930979643..1809c07fb 100644
--- a/crypto/test/src/crypto/test/DigestTest.cs
+++ b/crypto/test/src/crypto/test/DigestTest.cs
@@ -1,7 +1,6 @@
 using System;
 
-using Org.BouncyCastle.Crypto;
-
+using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Encoders;
 using Org.BouncyCastle.Utilities.Test;
@@ -11,6 +10,8 @@ namespace Org.BouncyCastle.Crypto.Tests
 	public abstract class DigestTest
 		: SimpleTest
 	{
+		internal static readonly SecureRandom Random = new SecureRandom();
+
 		private IDigest digest;
 		private string[] input;
 		private string[] results;
@@ -108,6 +109,8 @@ namespace Org.BouncyCastle.Crypto.Tests
 			{
 				Fail("failing memo copy vector test", results[results.Length - 1], Hex.ToHexString(resBuf));
 			}
+
+			SpanConsistencyTests(this, digest);
 		}
 
 		private byte[] toByteArray(
@@ -179,5 +182,57 @@ namespace Org.BouncyCastle.Crypto.Tests
 				Fail("64k test failed");
 			}
 		}
+
+		internal static void SpanConsistencyTests(SimpleTest test, IDigest digest)
+        {
+			// NOTE: .NET Core 2.1 has Span<T>, but is tested against our .NET Standard 2.0 assembly.
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+
+			// Span-based API consistency checks
+			byte[] data = new byte[16 + 256];
+			Random.NextBytes(data);
+
+			for (int len = 0; len <= 256; ++len)
+			{
+				int off = Random.Next(0, 17);
+
+				SpanConsistencyTest(test, digest, data, off, len);
+			}
+#endif
+		}
+
+		internal static void SpanConsistencyTest(SimpleTest test, IDigest digest, byte[] buf, int off, int len)
+        {
+			// NOTE: .NET Core 2.1 has Span<T>, but is tested against our .NET Standard 2.0 assembly.
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			digest.Reset();
+
+			byte[] arrayResult = DigestUtilities.DoFinal(digest, buf, off, len);
+			byte[] spanResult1 = DigestUtilities.DoFinal(digest, buf.AsSpan(off, len));
+
+			if (!test.AreEqual(arrayResult, spanResult1))
+			{
+				test.Fail("failing span consistency test 1", Hex.ToHexString(arrayResult), Hex.ToHexString(spanResult1));
+			}
+
+			int pos = 0;
+			while (pos < len)
+			{
+				int next = 1 + Random.Next(len - pos);
+				digest.BlockUpdate(buf.AsSpan(off + pos, next));
+				pos += next;
+			}
+
+			byte[] spanResult2 = new byte[digest.GetDigestSize()];
+			digest.DoFinal(spanResult2.AsSpan());
+
+			if (!test.AreEqual(arrayResult, spanResult2))
+			{
+				test.Fail("failing span consistency test 2", Hex.ToHexString(arrayResult), Hex.ToHexString(spanResult2));
+			}
+#endif
+		}
 	}
 }
diff --git a/crypto/test/src/crypto/test/GcmSivTest.cs b/crypto/test/src/crypto/test/GcmSivTest.cs
index 16e53abdf..47f824db6 100644
--- a/crypto/test/src/crypto/test/GcmSivTest.cs
+++ b/crypto/test/src/crypto/test/GcmSivTest.cs
@@ -74,6 +74,15 @@ namespace Org.BouncyCastle.Crypto.Tests
                 pCipher.DoFinal(myOutput, 0);
                 IsTrue("Encryption mismatch", Arrays.AreEqual(myExpected, myOutput));
 
+                if (myData.Length >= 2)
+                {
+                    /* Repeat processing checking processBytes with non-empty internal buffer */
+                    pCipher.ProcessByte(myData[0], null, 0);
+                    pCipher.ProcessBytes(myData, 1, myData.Length - 1, null, 0);
+                    pCipher.DoFinal(myOutput, 0);
+                    IsTrue("Encryption mismatch", Arrays.AreEqual(myExpected, myOutput));
+                }
+
                 /* Re-initialise the cipher */
                 pCipher.Init(false, myParams);
                 pCipher.ProcessBytes(myOutput, 0, myOutput.Length, null, 0);
diff --git a/crypto/test/src/crypto/test/Haraka256DigestTest.cs b/crypto/test/src/crypto/test/Haraka256DigestTest.cs
index 3f7706211..18ff65ddc 100644
--- a/crypto/test/src/crypto/test/Haraka256DigestTest.cs
+++ b/crypto/test/src/crypto/test/Haraka256DigestTest.cs
@@ -2,6 +2,7 @@
 using NUnit.Framework;
 
 using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Encoders;
 using Org.BouncyCastle.Utilities.Test;
@@ -185,6 +186,8 @@ namespace Org.BouncyCastle.Crypto.Tests
             TestInputTooShort();
             TestOutput();
             TestMonty();
+
+            SpanConsistencyTests();
         }
 
         [Test]
@@ -194,5 +197,22 @@ namespace Org.BouncyCastle.Crypto.Tests
 
             Assert.AreEqual(Name + ": Okay", resultText);
         }
+
+        private void SpanConsistencyTests()
+        {
+            // NOTE: .NET Core 2.1 has Span<T>, but is tested against our .NET Standard 2.0 assembly.
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            // Span-based API consistency checks
+            byte[] data = new byte[16 + 32];
+            DigestTest.Random.NextBytes(data);
+
+            var digest = new Haraka256Digest();
+            for (int off = 0; off <= 16; ++off)
+            {
+                DigestTest.SpanConsistencyTest(this, digest, data, off, 32);
+            }
+#endif
+        }
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/test/src/crypto/test/Haraka512DigestTest.cs b/crypto/test/src/crypto/test/Haraka512DigestTest.cs
index 11aa746af..1ef178222 100644
--- a/crypto/test/src/crypto/test/Haraka512DigestTest.cs
+++ b/crypto/test/src/crypto/test/Haraka512DigestTest.cs
@@ -188,6 +188,8 @@ namespace Org.BouncyCastle.Crypto.Tests
             TestInputTooShort();
             TestOutput();
             TestMonty();
+
+            SpanConsistencyTests();
         }
 
         [Test]
@@ -197,5 +199,22 @@ namespace Org.BouncyCastle.Crypto.Tests
 
             Assert.AreEqual(Name + ": Okay", resultText);
         }
+
+        private void SpanConsistencyTests()
+        {
+            // NOTE: .NET Core 2.1 has Span<T>, but is tested against our .NET Standard 2.0 assembly.
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            // Span-based API consistency checks
+            byte[] data = new byte[16 + 64];
+            DigestTest.Random.NextBytes(data);
+
+            var digest = new Haraka512Digest();
+            for (int off = 0; off <= 16; ++off)
+            {
+                DigestTest.SpanConsistencyTest(this, digest, data, off, 64);
+            }
+#endif
+        }
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/test/src/crypto/test/KeccakDigestTest.cs b/crypto/test/src/crypto/test/KeccakDigestTest.cs
index ddabddab4..12b310fc0 100644
--- a/crypto/test/src/crypto/test/KeccakDigestTest.cs
+++ b/crypto/test/src/crypto/test/KeccakDigestTest.cs
@@ -273,6 +273,8 @@ namespace Org.BouncyCastle.Crypto.Tests
             //    Fail("Keccak mismatch on " + digest.AlgorithmName + " extreme data test");
             //}
             //Console.WriteLine("Done");
+
+            DigestTest.SpanConsistencyTests(this, digest);
         }
 
         private void TestDigestDoFinal(IDigest digest)
diff --git a/crypto/test/src/crypto/test/ParallelHashTest.cs b/crypto/test/src/crypto/test/ParallelHashTest.cs
index 536567313..e37516366 100644
--- a/crypto/test/src/crypto/test/ParallelHashTest.cs
+++ b/crypto/test/src/crypto/test/ParallelHashTest.cs
@@ -119,6 +119,9 @@ namespace Org.BouncyCastle.Crypto.Tests
             IsTrue("oops!", Arrays.AreEqual(Hex.Decode("6b3e790b330c889a204c2fbc728d809f19367328d852f4002dc829f73afd6bcefb7fe5b607b13a801c0be5c1170bdb794e339458fdb0e62a6af3d42558970249"), res));
 
             testEmpty();
+
+            DigestTest.SpanConsistencyTests(this, new ParallelHash(128, new byte[0], 8));
+            DigestTest.SpanConsistencyTests(this, new ParallelHash(256, new byte[0], 8));
         }
 
         private void testEmpty()
diff --git a/crypto/test/src/crypto/test/SHA3DigestTest.cs b/crypto/test/src/crypto/test/SHA3DigestTest.cs
index 7b9ab26cf..2984f1c83 100644
--- a/crypto/test/src/crypto/test/SHA3DigestTest.cs
+++ b/crypto/test/src/crypto/test/SHA3DigestTest.cs
@@ -40,6 +40,8 @@ namespace Org.BouncyCastle.Crypto.Tests
         public override void PerformTest()
         {
             TestVectors();
+
+            DigestTest.SpanConsistencyTests(this, new Sha3Digest());
         }
 
         public void TestVectors()
diff --git a/crypto/test/src/crypto/test/ShakeDigestTest.cs b/crypto/test/src/crypto/test/ShakeDigestTest.cs
index 4b4d0fbd6..0aeedb256 100644
--- a/crypto/test/src/crypto/test/ShakeDigestTest.cs
+++ b/crypto/test/src/crypto/test/ShakeDigestTest.cs
@@ -40,6 +40,8 @@ namespace Org.BouncyCastle.Crypto.Tests
         public override void PerformTest()
         {
             TestVectors();
+
+            DigestTest.SpanConsistencyTests(this, new ShakeDigest());
         }
 
         public void TestVectors()
diff --git a/crypto/test/src/crypto/test/ShortenedDigestTest.cs b/crypto/test/src/crypto/test/ShortenedDigestTest.cs
index 927ffee3a..01c408219 100644
--- a/crypto/test/src/crypto/test/ShortenedDigestTest.cs
+++ b/crypto/test/src/crypto/test/ShortenedDigestTest.cs
@@ -74,6 +74,8 @@ namespace Org.BouncyCastle.Crypto.Tests
 			{
 				// expected
 			}
+
+			DigestTest.SpanConsistencyTests(this, new ShortenedDigest(new Sha1Digest(), 10));
 		}
 
 		public override string Name
diff --git a/crypto/test/src/crypto/test/SkeinDigestTest.cs b/crypto/test/src/crypto/test/SkeinDigestTest.cs
index 50a2d9565..7935eaafb 100644
--- a/crypto/test/src/crypto/test/SkeinDigestTest.cs
+++ b/crypto/test/src/crypto/test/SkeinDigestTest.cs
@@ -205,6 +205,8 @@ namespace Org.BouncyCastle.Crypto.Tests
 				Case test = TEST_CASES[i];
 				runTest(test);
 			}
+
+			DigestTest.SpanConsistencyTests(this, new SkeinDigest(256, 256));
 		}
 
 		private void runTest(Case dc)
diff --git a/crypto/test/src/crypto/test/TupleHashTest.cs b/crypto/test/src/crypto/test/TupleHashTest.cs
index fd1ee001f..918fcbea4 100644
--- a/crypto/test/src/crypto/test/TupleHashTest.cs
+++ b/crypto/test/src/crypto/test/TupleHashTest.cs
@@ -3,6 +3,7 @@ using System;
 using NUnit.Framework;
 
 using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Encoders;
 using Org.BouncyCastle.Utilities.Test;
@@ -103,6 +104,8 @@ namespace Org.BouncyCastle.Crypto.Tests
 
             IsTrue("oops!", !Arrays.AreEqual(Hex.Decode("45 00 0B E6 3F 9B 6B FD 89 F5 47 17 67 0F 69 A9 BC 76 35 91 A4 F0 5C 50 D6 88 91 A7 44 BC C6 E7 D6 D5 B5 E8 2C 01 8D A9 99 ED 35 B0 BB 49 C9 67 8E 52 6A BD 8E 85 C1 3E D2 54 02 1D B9 E7 90 CE"), res));
             IsTrue("oops!", Arrays.AreEqual(Hex.Decode("0c59b11464f2336c34663ed51b2b950bec743610856f36c28d1d088d8a2446284dd09830a6a178dc752376199fae935d86cfdee5913d4922dfd369b66a53c897"), res));
+
+            SpanConsistencyTests();
         }
 
         [Test]
@@ -112,5 +115,64 @@ namespace Org.BouncyCastle.Crypto.Tests
 
             Assert.AreEqual(Name + ": Okay", resultText);
         }
+
+        internal void SpanConsistencyTests()
+        {
+            // NOTE: .NET Core 2.1 has Span<T>, but is tested against our .NET Standard 2.0 assembly.
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            IDigest digest1 = new TupleHash(128, new byte[0]);
+            IDigest digest2 = new TupleHash(128, new byte[0]);
+
+            // Span-based API consistency checks
+            byte[] data = new byte[16 + 256];
+            DigestTest.Random.NextBytes(data);
+
+            for (int len = 0; len <= 256; ++len)
+            {
+                int off = DigestTest.Random.Next(0, 17);
+
+                SpanConsistencyTest(digest1, digest2, data, off, len);
+            }
+#endif
+        }
+
+        internal void SpanConsistencyTest(IDigest digest1, IDigest digest2, byte[] buf, int off, int len)
+        {
+            // NOTE: .NET Core 2.1 has Span<T>, but is tested against our .NET Standard 2.0 assembly.
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            digest1.Reset();
+            digest2.Reset();
+
+            byte[] arrayResult1 = DigestUtilities.DoFinal(digest1, buf, off, len);
+            byte[] spanResult1 = DigestUtilities.DoFinal(digest2, buf.AsSpan(off, len));
+
+            if (!AreEqual(arrayResult1, spanResult1))
+            {
+                Fail("failing span consistency test 1", Hex.ToHexString(arrayResult1), Hex.ToHexString(spanResult1));
+            }
+
+            int pos = 0;
+            while (pos < len)
+            {
+                int next = 1 + DigestTest.Random.Next(len - pos);
+                digest1.BlockUpdate(buf, off + pos, next);
+                digest2.BlockUpdate(buf.AsSpan(off + pos, next));
+                pos += next;
+            }
+
+            byte[] arrayResult2 = new byte[digest1.GetDigestSize()];
+            digest1.DoFinal(arrayResult2, 0);
+
+            byte[] spanResult2 = new byte[digest2.GetDigestSize()];
+            digest2.DoFinal(spanResult2.AsSpan());
+
+            if (!AreEqual(arrayResult2, spanResult2))
+            {
+                Fail("failing span consistency test 2", Hex.ToHexString(arrayResult2), Hex.ToHexString(spanResult2));
+            }
+#endif
+        }
     }
 }