summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2022-08-24 00:50:53 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-08-24 00:50:53 +0700
commitd3037c2b2f4f76c05e12b4d8e2c3326601440d6f (patch)
tree138533d108a8033758fbad1a81e942904f4d0557
parentCleanup (diff)
downloadBouncyCastle.NET-ed25519-d3037c2b2f4f76c05e12b4d8e2c3326601440d6f.tar.xz
Span-based variant for IBlockCipher.ProcessBlock
-rw-r--r--crypto/src/crypto/IBlockCipher.cs16
-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/CfbBlockCipherMac.cs69
-rw-r--r--crypto/src/crypto/macs/DSTU7564Mac.cs4
-rw-r--r--crypto/src/crypto/modes/CbcBlockCipher.cs149
-rw-r--r--crypto/src/crypto/modes/CcmBlockCipher.cs2
-rw-r--r--crypto/src/crypto/modes/CfbBlockCipher.cs136
-rw-r--r--crypto/src/crypto/modes/GOFBBlockCipher.cs107
-rw-r--r--crypto/src/crypto/modes/GcmSivBlockCipher.cs4
-rw-r--r--crypto/src/crypto/modes/KCtrBlockCipher.cs55
-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/test/src/crypto/prng/test/CtrDrbgTest.cs9
43 files changed, 3281 insertions, 1391 deletions
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/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/CfbBlockCipherMac.cs b/crypto/src/crypto/macs/CfbBlockCipherMac.cs
index 364cf8499..e10bb438d 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;
 
diff --git a/crypto/src/crypto/macs/DSTU7564Mac.cs b/crypto/src/crypto/macs/DSTU7564Mac.cs
index 36e86418a..fc905cc99 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");
@@ -78,7 +78,7 @@ namespace Org.BouncyCastle.Crypto.Macs
 
         public int DoFinal(byte[] output, int outOff)
         {
-            Check.OutputLength(output, outOff, macSize, "Output buffer too short");
+            Check.OutputLength(output, outOff, macSize, "output buffer too short");
 
             if (paddedKey == null)
                 throw new InvalidOperationException(AlgorithmName + " not initialised");
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..8f0acce52 100644
--- a/crypto/src/crypto/modes/CcmBlockCipher.cs
+++ b/crypto/src/crypto/modes/CcmBlockCipher.cs
@@ -134,7 +134,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/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..2ea8eef1d 100644
--- a/crypto/src/crypto/modes/GcmSivBlockCipher.cs
+++ b/crypto/src/crypto/modes/GcmSivBlockCipher.cs
@@ -472,8 +472,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.");
             }
         }
 
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/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/test/src/crypto/prng/test/CtrDrbgTest.cs b/crypto/test/src/crypto/prng/test/CtrDrbgTest.cs
index 65209abdb..3e90c5752 100644
--- a/crypto/test/src/crypto/prng/test/CtrDrbgTest.cs
+++ b/crypto/test/src/crypto/prng/test/CtrDrbgTest.cs
@@ -512,6 +512,15 @@ namespace Org.BouncyCastle.Crypto.Prng.Test
                 return cipher.ProcessBlock(input, inOff, output, outOff);
             }
 
+            // NOTE: .NET Core 2.1 has Span<T>, but is tested against our .NET Standard 2.0 assembly.
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
+            {
+                return cipher.ProcessBlock(input, output);
+            }
+#endif
+
             public void Reset()
             {
                 cipher.Reset();