summary refs log tree commit diff
path: root/crypto/src
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/src
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/src')
-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
141 files changed, 6994 insertions, 2361 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
         }
     }
 }