summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2022-10-10 23:08:07 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-10-10 23:08:07 +0700
commit44ccb96f7e859ee1f07d3ca032c59c88b92b09b4 (patch)
tree082196f723c3aac9519526ab555670729d482528
parentRefactoring pass over Pqc.Crypto.SphincsPlus (diff)
downloadBouncyCastle.NET-ed25519-44ccb96f7e859ee1f07d3ca032c59c88b92b09b4.tar.xz
Refactor Haraka digests
-rw-r--r--crypto/src/crypto/digests/Haraka256Digest.cs319
-rw-r--r--crypto/src/crypto/digests/Haraka512Digest.cs441
-rw-r--r--crypto/src/crypto/digests/HarakaBase.cs183
3 files changed, 439 insertions, 504 deletions
diff --git a/crypto/src/crypto/digests/Haraka256Digest.cs b/crypto/src/crypto/digests/Haraka256Digest.cs
index 27aea9b29..2507125b2 100644
--- a/crypto/src/crypto/digests/Haraka256Digest.cs
+++ b/crypto/src/crypto/digests/Haraka256Digest.cs
@@ -1,231 +1,80 @@
 using System;
 
-
 namespace Org.BouncyCastle.Crypto.Digests
 {
-    public class Haraka256Digest : HarakaBase
+    public sealed class Haraka256Digest
+        : HarakaBase
     {
-        private static readonly byte[][] RC = new byte[][]{
-        new byte[]{(byte)0x06, (byte)0x84, (byte)0x70, (byte)0x4c, (byte)0xe6, (byte)0x20, (byte)0xc0, (byte)0x0a, (byte)0xb2, (byte)0xc5, (byte)0xfe, (byte)0xf0, (byte)0x75, (byte)0x81, (byte)0x7b, (byte)0x9d},
-        new byte[]{(byte)0x8b, (byte)0x66, (byte)0xb4, (byte)0xe1, (byte)0x88, (byte)0xf3, (byte)0xa0, (byte)0x6b, (byte)0x64, (byte)0x0f, (byte)0x6b, (byte)0xa4, (byte)0x2f, (byte)0x08, (byte)0xf7, (byte)0x17},
-        new byte[]{(byte)0x34, (byte)0x02, (byte)0xde, (byte)0x2d, (byte)0x53, (byte)0xf2, (byte)0x84, (byte)0x98, (byte)0xcf, (byte)0x02, (byte)0x9d, (byte)0x60, (byte)0x9f, (byte)0x02, (byte)0x91, (byte)0x14},
-        new byte[]{(byte)0x0e, (byte)0xd6, (byte)0xea, (byte)0xe6, (byte)0x2e, (byte)0x7b, (byte)0x4f, (byte)0x08, (byte)0xbb, (byte)0xf3, (byte)0xbc, (byte)0xaf, (byte)0xfd, (byte)0x5b, (byte)0x4f, (byte)0x79},
-        new byte[]{(byte)0xcb, (byte)0xcf, (byte)0xb0, (byte)0xcb, (byte)0x48, (byte)0x72, (byte)0x44, (byte)0x8b, (byte)0x79, (byte)0xee, (byte)0xcd, (byte)0x1c, (byte)0xbe, (byte)0x39, (byte)0x70, (byte)0x44},
-        new byte[]{(byte)0x7e, (byte)0xea, (byte)0xcd, (byte)0xee, (byte)0x6e, (byte)0x90, (byte)0x32, (byte)0xb7, (byte)0x8d, (byte)0x53, (byte)0x35, (byte)0xed, (byte)0x2b, (byte)0x8a, (byte)0x05, (byte)0x7b},
-        new byte[]{(byte)0x67, (byte)0xc2, (byte)0x8f, (byte)0x43, (byte)0x5e, (byte)0x2e, (byte)0x7c, (byte)0xd0, (byte)0xe2, (byte)0x41, (byte)0x27, (byte)0x61, (byte)0xda, (byte)0x4f, (byte)0xef, (byte)0x1b},
-        new byte[]{(byte)0x29, (byte)0x24, (byte)0xd9, (byte)0xb0, (byte)0xaf, (byte)0xca, (byte)0xcc, (byte)0x07, (byte)0x67, (byte)0x5f, (byte)0xfd, (byte)0xe2, (byte)0x1f, (byte)0xc7, (byte)0x0b, (byte)0x3b},
-        new byte[]{(byte)0xab, (byte)0x4d, (byte)0x63, (byte)0xf1, (byte)0xe6, (byte)0x86, (byte)0x7f, (byte)0xe9, (byte)0xec, (byte)0xdb, (byte)0x8f, (byte)0xca, (byte)0xb9, (byte)0xd4, (byte)0x65, (byte)0xee},
-        new byte[]{(byte)0x1c, (byte)0x30, (byte)0xbf, (byte)0x84, (byte)0xd4, (byte)0xb7, (byte)0xcd, (byte)0x64, (byte)0x5b, (byte)0x2a, (byte)0x40, (byte)0x4f, (byte)0xad, (byte)0x03, (byte)0x7e, (byte)0x33},
-        new byte[]{(byte)0xb2, (byte)0xcc, (byte)0x0b, (byte)0xb9, (byte)0x94, (byte)0x17, (byte)0x23, (byte)0xbf, (byte)0x69, (byte)0x02, (byte)0x8b, (byte)0x2e, (byte)0x8d, (byte)0xf6, (byte)0x98, (byte)0x00},
-        new byte[]{(byte)0xfa, (byte)0x04, (byte)0x78, (byte)0xa6, (byte)0xde, (byte)0x6f, (byte)0x55, (byte)0x72, (byte)0x4a, (byte)0xaa, (byte)0x9e, (byte)0xc8, (byte)0x5c, (byte)0x9d, (byte)0x2d, (byte)0x8a},
-        new byte[]{(byte)0xdf, (byte)0xb4, (byte)0x9f, (byte)0x2b, (byte)0x6b, (byte)0x77, (byte)0x2a, (byte)0x12, (byte)0x0e, (byte)0xfa, (byte)0x4f, (byte)0x2e, (byte)0x29, (byte)0x12, (byte)0x9f, (byte)0xd4},
-        new byte[]{(byte)0x1e, (byte)0xa1, (byte)0x03, (byte)0x44, (byte)0xf4, (byte)0x49, (byte)0xa2, (byte)0x36, (byte)0x32, (byte)0xd6, (byte)0x11, (byte)0xae, (byte)0xbb, (byte)0x6a, (byte)0x12, (byte)0xee},
-        new byte[]{(byte)0xaf, (byte)0x04, (byte)0x49, (byte)0x88, (byte)0x4b, (byte)0x05, (byte)0x00, (byte)0x84, (byte)0x5f, (byte)0x96, (byte)0x00, (byte)0xc9, (byte)0x9c, (byte)0xa8, (byte)0xec, (byte)0xa6},
-        new byte[]{(byte)0x21, (byte)0x02, (byte)0x5e, (byte)0xd8, (byte)0x9d, (byte)0x19, (byte)0x9c, (byte)0x4f, (byte)0x78, (byte)0xa2, (byte)0xc7, (byte)0xe3, (byte)0x27, (byte)0xe5, (byte)0x93, (byte)0xec},
-        new byte[]{(byte)0xbf, (byte)0x3a, (byte)0xaa, (byte)0xf8, (byte)0xa7, (byte)0x59, (byte)0xc9, (byte)0xb7, (byte)0xb9, (byte)0x28, (byte)0x2e, (byte)0xcd, (byte)0x82, (byte)0xd4, (byte)0x01, (byte)0x73},
-        new byte[]{(byte)0x62, (byte)0x60, (byte)0x70, (byte)0x0d, (byte)0x61, (byte)0x86, (byte)0xb0, (byte)0x17, (byte)0x37, (byte)0xf2, (byte)0xef, (byte)0xd9, (byte)0x10, (byte)0x30, (byte)0x7d, (byte)0x6b},
-        new byte[]{(byte)0x5a, (byte)0xca, (byte)0x45, (byte)0xc2, (byte)0x21, (byte)0x30, (byte)0x04, (byte)0x43, (byte)0x81, (byte)0xc2, (byte)0x91, (byte)0x53, (byte)0xf6, (byte)0xfc, (byte)0x9a, (byte)0xc6},
-        new byte[]{(byte)0x92, (byte)0x23, (byte)0x97, (byte)0x3c, (byte)0x22, (byte)0x6b, (byte)0x68, (byte)0xbb, (byte)0x2c, (byte)0xaf, (byte)0x92, (byte)0xe8, (byte)0x36, (byte)0xd1, (byte)0x94, (byte)0x3a}
-    };
-        private readonly byte[] buffer;
-        private int off;
-        private void mix256(byte[][] s1, byte[][] s2)
-        {
-            Array.Copy(s1[0], 0, s2[0], 0, 4);
-            Array.Copy(s1[1], 0, s2[0], 4, 4);
-            Array.Copy(s1[0], 4, s2[0], 8, 4);
-            Array.Copy(s1[1], 4, s2[0], 12, 4);
-
-            Array.Copy(s1[0], 8, s2[1], 0, 4);
-            Array.Copy(s1[1], 8, s2[1], 4, 4);
-            Array.Copy(s1[0], 12, s2[1], 8, 4);
-            Array.Copy(s1[1], 12, s2[1], 12, 4);
-        }
-
-        private int haraka256256(byte[] msg, byte[] output, int outOff)
-        {
-            byte[][] s1 = new byte[2][];
-            s1[0] = new byte[16];
-            s1[1] = new byte[16];
-            byte[][] s2 = new byte[2][];
-            s2[0] = new byte[16];
-            s2[1] = new byte[16];
-
-            Array.Copy(msg, 0, s1[0], 0, 16);
-            Array.Copy(msg, 16, s1[1], 0, 16);
-
-            s1[0] = aesEnc(s1[0], RC[0]);
-            s1[1] = aesEnc(s1[1], RC[1]);
-            s1[0] = aesEnc(s1[0], RC[2]);
-            s1[1] = aesEnc(s1[1], RC[3]);
-            mix256(s1, s2);
-
-            s1[0] = aesEnc(s2[0], RC[4]);
-            s1[1] = aesEnc(s2[1], RC[5]);
-            s1[0] = aesEnc(s1[0], RC[6]);
-            s1[1] = aesEnc(s1[1], RC[7]);
-            mix256(s1, s2);
-
-            s1[0] = aesEnc(s2[0], RC[8]);
-            s1[1] = aesEnc(s2[1], RC[9]);
-            s1[0] = aesEnc(s1[0], RC[10]);
-            s1[1] = aesEnc(s1[1], RC[11]);
-            mix256(s1, s2);
-
-            s1[0] = aesEnc(s2[0], RC[12]);
-            s1[1] = aesEnc(s2[1], RC[13]);
-            s1[0] = aesEnc(s1[0], RC[14]);
-            s1[1] = aesEnc(s1[1], RC[15]);
-            mix256(s1, s2);
-
-            s1[0] = aesEnc(s2[0], RC[16]);
-            s1[1] = aesEnc(s2[1], RC[17]);
-            s1[0] = aesEnc(s1[0], RC[18]);
-            s1[1] = aesEnc(s1[1], RC[19]);
-            mix256(s1, s2);
-
-            s1[0] = Xor(s2[0], msg, 0);
-            s1[1] = Xor(s2[1], msg, 16);
-
-            Array.Copy(s1[0], 0, output, outOff, 16);
-            Array.Copy(s1[1], 0, output, outOff + 16, 16);
-
-            return DIGEST_SIZE;
-        }
-
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        private int haraka256256(byte[] msg, Span<byte> output)
-        {
-            byte[][] s1 = new byte[2][];
-            s1[0] = new byte[16];
-            s1[1] = new byte[16];
-            byte[][] s2 = new byte[2][];
-            s2[0] = new byte[16];
-            s2[1] = new byte[16];
-
-            Array.Copy(msg, 0, s1[0], 0, 16);
-            Array.Copy(msg, 16, s1[1], 0, 16);
-
-            s1[0] = aesEnc(s1[0], RC[0]);
-            s1[1] = aesEnc(s1[1], RC[1]);
-            s1[0] = aesEnc(s1[0], RC[2]);
-            s1[1] = aesEnc(s1[1], RC[3]);
-            mix256(s1, s2);
-
-            s1[0] = aesEnc(s2[0], RC[4]);
-            s1[1] = aesEnc(s2[1], RC[5]);
-            s1[0] = aesEnc(s1[0], RC[6]);
-            s1[1] = aesEnc(s1[1], RC[7]);
-            mix256(s1, s2);
-
-            s1[0] = aesEnc(s2[0], RC[8]);
-            s1[1] = aesEnc(s2[1], RC[9]);
-            s1[0] = aesEnc(s1[0], RC[10]);
-            s1[1] = aesEnc(s1[1], RC[11]);
-            mix256(s1, s2);
-
-            s1[0] = aesEnc(s2[0], RC[12]);
-            s1[1] = aesEnc(s2[1], RC[13]);
-            s1[0] = aesEnc(s1[0], RC[14]);
-            s1[1] = aesEnc(s1[1], RC[15]);
-            mix256(s1, s2);
-
-            s1[0] = aesEnc(s2[0], RC[16]);
-            s1[1] = aesEnc(s2[1], RC[17]);
-            s1[0] = aesEnc(s1[0], RC[18]);
-            s1[1] = aesEnc(s1[1], RC[19]);
-            mix256(s1, s2);
-
-            s1[0] = Xor(s2[0], msg, 0);
-            s1[1] = Xor(s2[1], msg, 16);
-
-            s1[0].AsSpan(0, 16).CopyTo(output);
-            s1[1].AsSpan(0, 16).CopyTo(output[16..]);
-
-            return DIGEST_SIZE;
-        }
-#endif
+        private readonly byte[] m_buf;
+        private int m_bufPos;
 
         public Haraka256Digest()
         {
-            this.buffer = new byte[32];
-        }
-
-        public Haraka256Digest(Haraka256Digest digest)
-        {
-            this.buffer = (byte[]) digest.buffer.Clone();
-            this.off = digest.off;
+            m_buf = new byte[32];
+            m_bufPos = 0;
         }
 
         public override string AlgorithmName => "Haraka-256";
 
+        public override int GetByteLength() => 32;
+
         public override void Update(byte input)
         {
-            if (off + 1 > 32)
-            {
+            if (m_bufPos > 32 - 1)
                 throw new ArgumentException("total input cannot be more than 32 bytes");
-            }
 
-            buffer[off++] = input;
+            m_buf[m_bufPos++] = input;
         }
 
         public override void BlockUpdate(byte[] input, int inOff, int len)
         {
-            if (off + len > 32)
-            {
+            if (m_bufPos > 32 - len)
                 throw new ArgumentException("total input cannot be more than 32 bytes");
-            }
 
-            Array.Copy(input, inOff, buffer, off, len);
-            off += len;
+            Array.Copy(input, inOff, m_buf, m_bufPos, len);
+            m_bufPos += len;
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         public override void BlockUpdate(ReadOnlySpan<byte> input)
         {
-            if (off + input.Length > 32)
-            {
+            if (m_bufPos > 32 - input.Length)
                 throw new ArgumentException("total input cannot be more than 32 bytes");
-            }
 
-            input.CopyTo(buffer.AsSpan(off));
-            off += input.Length;
+            input.CopyTo(m_buf.AsSpan(m_bufPos));
+            m_bufPos += input.Length;
         }
 #endif
 
         public override int DoFinal(byte[] output, int outOff)
         {
-            if (off != 32)
-            {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return DoFinal(output.AsSpan(outOff));
+#else
+            if (m_bufPos != 32)
                 throw new ArgumentException("input must be exactly 32 bytes");
-            }
 
             if (output.Length - outOff < 32)
-            {
                 throw new ArgumentException("output too short to receive digest");
-            }
 
-            int rv = haraka256256(buffer, output, outOff);
+            int rv = Haraka256256(m_buf, output, outOff);
 
             Reset();
 
             return rv;
+#endif
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         public override int DoFinal(Span<byte> output)
         {
-            if (off != 32)
-            {
+            if (m_bufPos != 32)
                 throw new ArgumentException("input must be exactly 32 bytes");
-            }
 
             if (output.Length < 32)
-            {
                 throw new ArgumentException("output too short to receive digest");
-            }
 
-            int rv = haraka256256(buffer, output);
+            int rv = Haraka256256(m_buf, output);
 
             Reset();
 
@@ -235,8 +84,122 @@ namespace Org.BouncyCastle.Crypto.Digests
 
         public override void Reset()
         {
-            off = 0;
-            Array.Clear(buffer, 0, 32);
+            m_bufPos = 0;
+            Array.Clear(m_buf, 0, 32);
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private static int Haraka256256(ReadOnlySpan<byte> msg, Span<byte> output)
+        {
+            byte[][] s1 = new byte[2][];
+            s1[0] = new byte[16];
+            s1[1] = new byte[16];
+            byte[][] s2 = new byte[2][];
+            s2[0] = new byte[16];
+            s2[1] = new byte[16];
+
+            msg[  ..16].CopyTo(s1[0]);
+            msg[16..32].CopyTo(s1[1]);
+
+            s1[0] = AesEnc(s1[0], RC[0]);
+            s1[1] = AesEnc(s1[1], RC[1]);
+            s1[0] = AesEnc(s1[0], RC[2]);
+            s1[1] = AesEnc(s1[1], RC[3]);
+            Mix256(s1, s2);
+
+            s1[0] = AesEnc(s2[0], RC[4]);
+            s1[1] = AesEnc(s2[1], RC[5]);
+            s1[0] = AesEnc(s1[0], RC[6]);
+            s1[1] = AesEnc(s1[1], RC[7]);
+            Mix256(s1, s2);
+
+            s1[0] = AesEnc(s2[0], RC[8]);
+            s1[1] = AesEnc(s2[1], RC[9]);
+            s1[0] = AesEnc(s1[0], RC[10]);
+            s1[1] = AesEnc(s1[1], RC[11]);
+            Mix256(s1, s2);
+
+            s1[0] = AesEnc(s2[0], RC[12]);
+            s1[1] = AesEnc(s2[1], RC[13]);
+            s1[0] = AesEnc(s1[0], RC[14]);
+            s1[1] = AesEnc(s1[1], RC[15]);
+            Mix256(s1, s2);
+
+            s1[0] = AesEnc(s2[0], RC[16]);
+            s1[1] = AesEnc(s2[1], RC[17]);
+            s1[0] = AesEnc(s1[0], RC[18]);
+            s1[1] = AesEnc(s1[1], RC[19]);
+            Mix256(s1, s2);
+
+            Xor(s2[0], msg      , output[  ..16]);
+            Xor(s2[1], msg[16..], output[16..32]);
+
+            return DIGEST_SIZE;
+        }
+#else
+        private static int Haraka256256(byte[] msg, byte[] output, int outOff)
+        {
+            byte[][] s1 = new byte[2][];
+            s1[0] = new byte[16];
+            s1[1] = new byte[16];
+            byte[][] s2 = new byte[2][];
+            s2[0] = new byte[16];
+            s2[1] = new byte[16];
+
+            Array.Copy(msg,  0, s1[0], 0, 16);
+            Array.Copy(msg, 16, s1[1], 0, 16);
+
+            s1[0] = AesEnc(s1[0], RC[0]);
+            s1[1] = AesEnc(s1[1], RC[1]);
+            s1[0] = AesEnc(s1[0], RC[2]);
+            s1[1] = AesEnc(s1[1], RC[3]);
+            Mix256(s1, s2);
+
+            s1[0] = AesEnc(s2[0], RC[4]);
+            s1[1] = AesEnc(s2[1], RC[5]);
+            s1[0] = AesEnc(s1[0], RC[6]);
+            s1[1] = AesEnc(s1[1], RC[7]);
+            Mix256(s1, s2);
+
+            s1[0] = AesEnc(s2[0], RC[8]);
+            s1[1] = AesEnc(s2[1], RC[9]);
+            s1[0] = AesEnc(s1[0], RC[10]);
+            s1[1] = AesEnc(s1[1], RC[11]);
+            Mix256(s1, s2);
+
+            s1[0] = AesEnc(s2[0], RC[12]);
+            s1[1] = AesEnc(s2[1], RC[13]);
+            s1[0] = AesEnc(s1[0], RC[14]);
+            s1[1] = AesEnc(s1[1], RC[15]);
+            Mix256(s1, s2);
+
+            s1[0] = AesEnc(s2[0], RC[16]);
+            s1[1] = AesEnc(s2[1], RC[17]);
+            s1[0] = AesEnc(s1[0], RC[18]);
+            s1[1] = AesEnc(s1[1], RC[19]);
+            Mix256(s1, s2);
+
+            s1[0] = Xor(s2[0], msg,  0);
+            s1[1] = Xor(s2[1], msg, 16);
+
+            Array.Copy(s1[0], 0, output, outOff     , 16);
+            Array.Copy(s1[1], 0, output, outOff + 16, 16);
+
+            return DIGEST_SIZE;
+        }
+#endif
+
+        private static void Mix256(byte[][] s1, byte[][] s2)
+        {
+            Array.Copy(s1[0], 0, s2[0], 0, 4);
+            Array.Copy(s1[1], 0, s2[0], 4, 4);
+            Array.Copy(s1[0], 4, s2[0], 8, 4);
+            Array.Copy(s1[1], 4, s2[0], 12, 4);
+
+            Array.Copy(s1[0], 8, s2[1], 0, 4);
+            Array.Copy(s1[1], 8, s2[1], 4, 4);
+            Array.Copy(s1[0], 12, s2[1], 8, 4);
+            Array.Copy(s1[1], 12, s2[1], 12, 4);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/src/crypto/digests/Haraka512Digest.cs b/crypto/src/crypto/digests/Haraka512Digest.cs
index 0faeae710..540ab6446 100644
--- a/crypto/src/crypto/digests/Haraka512Digest.cs
+++ b/crypto/src/crypto/digests/Haraka512Digest.cs
@@ -1,92 +1,95 @@
 using System;
 
-using Org.BouncyCastle.Utilities;
-
 namespace Org.BouncyCastle.Crypto.Digests
 {
-    public class Haraka512Digest : HarakaBase
+    public sealed class Haraka512Digest
+        : HarakaBase
     {
-        private static readonly byte[][] RC = new byte[][]{
-            new byte[]{(byte)0x06, (byte)0x84, (byte)0x70, (byte)0x4c, (byte)0xe6, (byte)0x20, (byte)0xc0, (byte)0x0a, (byte)0xb2, (byte)0xc5, (byte)0xfe, (byte)0xf0, (byte)0x75, (byte)0x81, (byte)0x7b, (byte)0x9d},
-            new byte[]{(byte)0x8b, (byte)0x66, (byte)0xb4, (byte)0xe1, (byte)0x88, (byte)0xf3, (byte)0xa0, (byte)0x6b, (byte)0x64, (byte)0x0f, (byte)0x6b, (byte)0xa4, (byte)0x2f, (byte)0x08, (byte)0xf7, (byte)0x17},
-            new byte[]{(byte)0x34, (byte)0x02, (byte)0xde, (byte)0x2d, (byte)0x53, (byte)0xf2, (byte)0x84, (byte)0x98, (byte)0xcf, (byte)0x02, (byte)0x9d, (byte)0x60, (byte)0x9f, (byte)0x02, (byte)0x91, (byte)0x14},
-            new byte[]{(byte)0x0e, (byte)0xd6, (byte)0xea, (byte)0xe6, (byte)0x2e, (byte)0x7b, (byte)0x4f, (byte)0x08, (byte)0xbb, (byte)0xf3, (byte)0xbc, (byte)0xaf, (byte)0xfd, (byte)0x5b, (byte)0x4f, (byte)0x79},
-            new byte[]{(byte)0xcb, (byte)0xcf, (byte)0xb0, (byte)0xcb, (byte)0x48, (byte)0x72, (byte)0x44, (byte)0x8b, (byte)0x79, (byte)0xee, (byte)0xcd, (byte)0x1c, (byte)0xbe, (byte)0x39, (byte)0x70, (byte)0x44},
-            new byte[]{(byte)0x7e, (byte)0xea, (byte)0xcd, (byte)0xee, (byte)0x6e, (byte)0x90, (byte)0x32, (byte)0xb7, (byte)0x8d, (byte)0x53, (byte)0x35, (byte)0xed, (byte)0x2b, (byte)0x8a, (byte)0x05, (byte)0x7b},
-            new byte[]{(byte)0x67, (byte)0xc2, (byte)0x8f, (byte)0x43, (byte)0x5e, (byte)0x2e, (byte)0x7c, (byte)0xd0, (byte)0xe2, (byte)0x41, (byte)0x27, (byte)0x61, (byte)0xda, (byte)0x4f, (byte)0xef, (byte)0x1b},
-            new byte[]{(byte)0x29, (byte)0x24, (byte)0xd9, (byte)0xb0, (byte)0xaf, (byte)0xca, (byte)0xcc, (byte)0x07, (byte)0x67, (byte)0x5f, (byte)0xfd, (byte)0xe2, (byte)0x1f, (byte)0xc7, (byte)0x0b, (byte)0x3b},
-            new byte[]{(byte)0xab, (byte)0x4d, (byte)0x63, (byte)0xf1, (byte)0xe6, (byte)0x86, (byte)0x7f, (byte)0xe9, (byte)0xec, (byte)0xdb, (byte)0x8f, (byte)0xca, (byte)0xb9, (byte)0xd4, (byte)0x65, (byte)0xee},
-            new byte[]{(byte)0x1c, (byte)0x30, (byte)0xbf, (byte)0x84, (byte)0xd4, (byte)0xb7, (byte)0xcd, (byte)0x64, (byte)0x5b, (byte)0x2a, (byte)0x40, (byte)0x4f, (byte)0xad, (byte)0x03, (byte)0x7e, (byte)0x33},
-            new byte[]{(byte)0xb2, (byte)0xcc, (byte)0x0b, (byte)0xb9, (byte)0x94, (byte)0x17, (byte)0x23, (byte)0xbf, (byte)0x69, (byte)0x02, (byte)0x8b, (byte)0x2e, (byte)0x8d, (byte)0xf6, (byte)0x98, (byte)0x00},
-            new byte[]{(byte)0xfa, (byte)0x04, (byte)0x78, (byte)0xa6, (byte)0xde, (byte)0x6f, (byte)0x55, (byte)0x72, (byte)0x4a, (byte)0xaa, (byte)0x9e, (byte)0xc8, (byte)0x5c, (byte)0x9d, (byte)0x2d, (byte)0x8a},
-            new byte[]{(byte)0xdf, (byte)0xb4, (byte)0x9f, (byte)0x2b, (byte)0x6b, (byte)0x77, (byte)0x2a, (byte)0x12, (byte)0x0e, (byte)0xfa, (byte)0x4f, (byte)0x2e, (byte)0x29, (byte)0x12, (byte)0x9f, (byte)0xd4},
-            new byte[]{(byte)0x1e, (byte)0xa1, (byte)0x03, (byte)0x44, (byte)0xf4, (byte)0x49, (byte)0xa2, (byte)0x36, (byte)0x32, (byte)0xd6, (byte)0x11, (byte)0xae, (byte)0xbb, (byte)0x6a, (byte)0x12, (byte)0xee},
-            new byte[]{(byte)0xaf, (byte)0x04, (byte)0x49, (byte)0x88, (byte)0x4b, (byte)0x05, (byte)0x00, (byte)0x84, (byte)0x5f, (byte)0x96, (byte)0x00, (byte)0xc9, (byte)0x9c, (byte)0xa8, (byte)0xec, (byte)0xa6},
-            new byte[]{(byte)0x21, (byte)0x02, (byte)0x5e, (byte)0xd8, (byte)0x9d, (byte)0x19, (byte)0x9c, (byte)0x4f, (byte)0x78, (byte)0xa2, (byte)0xc7, (byte)0xe3, (byte)0x27, (byte)0xe5, (byte)0x93, (byte)0xec},
-            new byte[]{(byte)0xbf, (byte)0x3a, (byte)0xaa, (byte)0xf8, (byte)0xa7, (byte)0x59, (byte)0xc9, (byte)0xb7, (byte)0xb9, (byte)0x28, (byte)0x2e, (byte)0xcd, (byte)0x82, (byte)0xd4, (byte)0x01, (byte)0x73},
-            new byte[]{(byte)0x62, (byte)0x60, (byte)0x70, (byte)0x0d, (byte)0x61, (byte)0x86, (byte)0xb0, (byte)0x17, (byte)0x37, (byte)0xf2, (byte)0xef, (byte)0xd9, (byte)0x10, (byte)0x30, (byte)0x7d, (byte)0x6b},
-            new byte[]{(byte)0x5a, (byte)0xca, (byte)0x45, (byte)0xc2, (byte)0x21, (byte)0x30, (byte)0x04, (byte)0x43, (byte)0x81, (byte)0xc2, (byte)0x91, (byte)0x53, (byte)0xf6, (byte)0xfc, (byte)0x9a, (byte)0xc6},
-            new byte[]{(byte)0x92, (byte)0x23, (byte)0x97, (byte)0x3c, (byte)0x22, (byte)0x6b, (byte)0x68, (byte)0xbb, (byte)0x2c, (byte)0xaf, (byte)0x92, (byte)0xe8, (byte)0x36, (byte)0xd1, (byte)0x94, (byte)0x3a},
-            new byte[]{(byte)0xd3, (byte)0xbf, (byte)0x92, (byte)0x38, (byte)0x22, (byte)0x58, (byte)0x86, (byte)0xeb, (byte)0x6c, (byte)0xba, (byte)0xb9, (byte)0x58, (byte)0xe5, (byte)0x10, (byte)0x71, (byte)0xb4},
-            new byte[]{(byte)0xdb, (byte)0x86, (byte)0x3c, (byte)0xe5, (byte)0xae, (byte)0xf0, (byte)0xc6, (byte)0x77, (byte)0x93, (byte)0x3d, (byte)0xfd, (byte)0xdd, (byte)0x24, (byte)0xe1, (byte)0x12, (byte)0x8d},
-            new byte[]{(byte)0xbb, (byte)0x60, (byte)0x62, (byte)0x68, (byte)0xff, (byte)0xeb, (byte)0xa0, (byte)0x9c, (byte)0x83, (byte)0xe4, (byte)0x8d, (byte)0xe3, (byte)0xcb, (byte)0x22, (byte)0x12, (byte)0xb1},
-            new byte[]{(byte)0x73, (byte)0x4b, (byte)0xd3, (byte)0xdc, (byte)0xe2, (byte)0xe4, (byte)0xd1, (byte)0x9c, (byte)0x2d, (byte)0xb9, (byte)0x1a, (byte)0x4e, (byte)0xc7, (byte)0x2b, (byte)0xf7, (byte)0x7d},
-            new byte[]{(byte)0x43, (byte)0xbb, (byte)0x47, (byte)0xc3, (byte)0x61, (byte)0x30, (byte)0x1b, (byte)0x43, (byte)0x4b, (byte)0x14, (byte)0x15, (byte)0xc4, (byte)0x2c, (byte)0xb3, (byte)0x92, (byte)0x4e},
-            new byte[]{(byte)0xdb, (byte)0xa7, (byte)0x75, (byte)0xa8, (byte)0xe7, (byte)0x07, (byte)0xef, (byte)0xf6, (byte)0x03, (byte)0xb2, (byte)0x31, (byte)0xdd, (byte)0x16, (byte)0xeb, (byte)0x68, (byte)0x99},
-            new byte[]{(byte)0x6d, (byte)0xf3, (byte)0x61, (byte)0x4b, (byte)0x3c, (byte)0x75, (byte)0x59, (byte)0x77, (byte)0x8e, (byte)0x5e, (byte)0x23, (byte)0x02, (byte)0x7e, (byte)0xca, (byte)0x47, (byte)0x2c},
-            new byte[]{(byte)0xcd, (byte)0xa7, (byte)0x5a, (byte)0x17, (byte)0xd6, (byte)0xde, (byte)0x7d, (byte)0x77, (byte)0x6d, (byte)0x1b, (byte)0xe5, (byte)0xb9, (byte)0xb8, (byte)0x86, (byte)0x17, (byte)0xf9},
-            new byte[]{(byte)0xec, (byte)0x6b, (byte)0x43, (byte)0xf0, (byte)0x6b, (byte)0xa8, (byte)0xe9, (byte)0xaa, (byte)0x9d, (byte)0x6c, (byte)0x06, (byte)0x9d, (byte)0xa9, (byte)0x46, (byte)0xee, (byte)0x5d},
-            new byte[]{(byte)0xcb, (byte)0x1e, (byte)0x69, (byte)0x50, (byte)0xf9, (byte)0x57, (byte)0x33, (byte)0x2b, (byte)0xa2, (byte)0x53, (byte)0x11, (byte)0x59, (byte)0x3b, (byte)0xf3, (byte)0x27, (byte)0xc1},
-            new byte[]{(byte)0x2c, (byte)0xee, (byte)0x0c, (byte)0x75, (byte)0x00, (byte)0xda, (byte)0x61, (byte)0x9c, (byte)0xe4, (byte)0xed, (byte)0x03, (byte)0x53, (byte)0x60, (byte)0x0e, (byte)0xd0, (byte)0xd9},
-            new byte[]{(byte)0xf0, (byte)0xb1, (byte)0xa5, (byte)0xa1, (byte)0x96, (byte)0xe9, (byte)0x0c, (byte)0xab, (byte)0x80, (byte)0xbb, (byte)0xba, (byte)0xbc, (byte)0x63, (byte)0xa4, (byte)0xa3, (byte)0x50},
-            new byte[]{(byte)0xae, (byte)0x3d, (byte)0xb1, (byte)0x02, (byte)0x5e, (byte)0x96, (byte)0x29, (byte)0x88, (byte)0xab, (byte)0x0d, (byte)0xde, (byte)0x30, (byte)0x93, (byte)0x8d, (byte)0xca, (byte)0x39},
-            new byte[]{(byte)0x17, (byte)0xbb, (byte)0x8f, (byte)0x38, (byte)0xd5, (byte)0x54, (byte)0xa4, (byte)0x0b, (byte)0x88, (byte)0x14, (byte)0xf3, (byte)0xa8, (byte)0x2e, (byte)0x75, (byte)0xb4, (byte)0x42},
-            new byte[]{(byte)0x34, (byte)0xbb, (byte)0x8a, (byte)0x5b, (byte)0x5f, (byte)0x42, (byte)0x7f, (byte)0xd7, (byte)0xae, (byte)0xb6, (byte)0xb7, (byte)0x79, (byte)0x36, (byte)0x0a, (byte)0x16, (byte)0xf6},
-            new byte[]{(byte)0x26, (byte)0xf6, (byte)0x52, (byte)0x41, (byte)0xcb, (byte)0xe5, (byte)0x54, (byte)0x38, (byte)0x43, (byte)0xce, (byte)0x59, (byte)0x18, (byte)0xff, (byte)0xba, (byte)0xaf, (byte)0xde},
-            new byte[]{(byte)0x4c, (byte)0xe9, (byte)0x9a, (byte)0x54, (byte)0xb9, (byte)0xf3, (byte)0x02, (byte)0x6a, (byte)0xa2, (byte)0xca, (byte)0x9c, (byte)0xf7, (byte)0x83, (byte)0x9e, (byte)0xc9, (byte)0x78},
-            new byte[]{(byte)0xae, (byte)0x51, (byte)0xa5, (byte)0x1a, (byte)0x1b, (byte)0xdf, (byte)0xf7, (byte)0xbe, (byte)0x40, (byte)0xc0, (byte)0x6e, (byte)0x28, (byte)0x22, (byte)0x90, (byte)0x12, (byte)0x35},
-            new byte[]{(byte)0xa0, (byte)0xc1, (byte)0x61, (byte)0x3c, (byte)0xba, (byte)0x7e, (byte)0xd2, (byte)0x2b, (byte)0xc1, (byte)0x73, (byte)0xbc, (byte)0x0f, (byte)0x48, (byte)0xa6, (byte)0x59, (byte)0xcf},
-            new byte[]{(byte)0x75, (byte)0x6a, (byte)0xcc, (byte)0x03, (byte)0x02, (byte)0x28, (byte)0x82, (byte)0x88, (byte)0x4a, (byte)0xd6, (byte)0xbd, (byte)0xfd, (byte)0xe9, (byte)0xc5, (byte)0x9d, (byte)0xa1}
-        };
-
-        private readonly byte[] buffer;
-        private int off;
+        private readonly byte[] m_buf;
+        private int m_bufPos;
 
         public Haraka512Digest()
         {
-            this.buffer = new byte[64];
+            m_buf = new byte[64];
+            m_bufPos = 0;
         }
 
-        public Haraka512Digest(Haraka512Digest digest)
+        public override string AlgorithmName => "Haraka-512";
+
+        public override int GetByteLength() => 64;
+
+        public override void Update(byte input)
         {
-            this.buffer = (byte[])digest.buffer.Clone();
-            this.off = digest.off;
+            if (m_bufPos > 64 - 1)
+                throw new ArgumentException("total input cannot be more than 64 bytes");
+
+            m_buf[m_bufPos++] = input;
         }
 
-        private void Mix512(byte[][] s1, byte[][] s2)
+        public override void BlockUpdate(byte[] input, int inOff, int len)
         {
-            Array.Copy(s1[0], 12, s2[0], 0, 4);
-            Array.Copy(s1[2], 12, s2[0], 4, 4);
-            Array.Copy(s1[1], 12, s2[0], 8, 4);
-            Array.Copy(s1[3], 12, s2[0], 12, 4);
+            if (m_bufPos > 64 - len)
+                throw new ArgumentException("total input cannot be more than 64 bytes");
 
-            Array.Copy(s1[2], 0, s2[1], 0, 4);
-            Array.Copy(s1[0], 0, s2[1], 4, 4);
-            Array.Copy(s1[3], 0, s2[1], 8, 4);
-            Array.Copy(s1[1], 0, s2[1], 12, 4);
+            Array.Copy(input, inOff, m_buf, m_bufPos, len);
+            m_bufPos += len;
+        }
 
-            Array.Copy(s1[2], 4, s2[2], 0, 4);
-            Array.Copy(s1[0], 4, s2[2], 4, 4);
-            Array.Copy(s1[3], 4, s2[2], 8, 4);
-            Array.Copy(s1[1], 4, s2[2], 12, 4);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            if (m_bufPos > 64 - input.Length)
+                throw new ArgumentException("total input cannot be more than 64 bytes");
 
-            Array.Copy(s1[0], 8, s2[3], 0, 4);
-            Array.Copy(s1[2], 8, s2[3], 4, 4);
-            Array.Copy(s1[1], 8, s2[3], 8, 4);
-            Array.Copy(s1[3], 8, s2[3], 12, 4);
+            input.CopyTo(m_buf.AsSpan(m_bufPos));
+            m_bufPos += input.Length;
+        }
+#endif
+
+        public override int DoFinal(byte[] output, int outOff)
+        {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return DoFinal(output.AsSpan(outOff));
+#else
+            if (m_bufPos != 64)
+                throw new ArgumentException("input must be exactly 64 bytes");
+
+            if (output.Length - outOff < 32)
+                throw new ArgumentException("output too short to receive digest");
+
+            int rv = Haraka512256(m_buf, output, outOff);
+
+            Reset();
+
+            return rv;
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int DoFinal(Span<byte> output)
+        {
+            if (m_bufPos != 64)
+                throw new ArgumentException("input must be exactly 64 bytes");
+
+            if (output.Length < 32)
+                throw new ArgumentException("output too short to receive digest");
+
+            int rv = Haraka512256(m_buf, output);
+
+            Reset();
+
+            return rv;
+        }
+#endif
+
+        public override void Reset()
+        {
+            m_bufPos = 0;
+            Array.Clear(m_buf, 0, 64);
         }
 
-        private int Haraka512256(byte[] msg, byte[] output, int outOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private static int Haraka512256(ReadOnlySpan<byte> msg, Span<byte> output)
         {
             byte[][] s1 = new byte[4][];
             s1[0] = new byte[16];
@@ -99,78 +102,75 @@ namespace Org.BouncyCastle.Crypto.Digests
             s2[2] = new byte[16];
             s2[3] = new byte[16];
 
-            //-- Unrolled version of above.
-
-            Array.Copy(msg, 0, s1[0], 0, 16);
-            Array.Copy(msg, 16, s1[1], 0, 16);
-            Array.Copy(msg, 32, s1[2], 0, 16);
-            Array.Copy(msg, 48, s1[3], 0, 16);
-
-            s1[0] = aesEnc(s1[0], RC[0]);
-            s1[1] = aesEnc(s1[1], RC[1]);
-            s1[2] = aesEnc(s1[2], RC[2]);
-            s1[3] = aesEnc(s1[3], RC[3]);
-            s1[0] = aesEnc(s1[0], RC[4]);
-            s1[1] = aesEnc(s1[1], RC[5]);
-            s1[2] = aesEnc(s1[2], RC[6]);
-            s1[3] = aesEnc(s1[3], RC[7]);
+            msg[  ..16].CopyTo(s1[0]);
+            msg[16..32].CopyTo(s1[1]);
+            msg[32..48].CopyTo(s1[2]);
+            msg[48..64].CopyTo(s1[3]);
+
+            s1[0] = AesEnc(s1[0], RC[0]);
+            s1[1] = AesEnc(s1[1], RC[1]);
+            s1[2] = AesEnc(s1[2], RC[2]);
+            s1[3] = AesEnc(s1[3], RC[3]);
+            s1[0] = AesEnc(s1[0], RC[4]);
+            s1[1] = AesEnc(s1[1], RC[5]);
+            s1[2] = AesEnc(s1[2], RC[6]);
+            s1[3] = AesEnc(s1[3], RC[7]);
             Mix512(s1, s2);
 
-            s1[0] = aesEnc(s2[0], RC[8]);
-            s1[1] = aesEnc(s2[1], RC[9]);
-            s1[2] = aesEnc(s2[2], RC[10]);
-            s1[3] = aesEnc(s2[3], RC[11]);
-            s1[0] = aesEnc(s1[0], RC[12]);
-            s1[1] = aesEnc(s1[1], RC[13]);
-            s1[2] = aesEnc(s1[2], RC[14]);
-            s1[3] = aesEnc(s1[3], RC[15]);
+            s1[0] = AesEnc(s2[0], RC[8]);
+            s1[1] = AesEnc(s2[1], RC[9]);
+            s1[2] = AesEnc(s2[2], RC[10]);
+            s1[3] = AesEnc(s2[3], RC[11]);
+            s1[0] = AesEnc(s1[0], RC[12]);
+            s1[1] = AesEnc(s1[1], RC[13]);
+            s1[2] = AesEnc(s1[2], RC[14]);
+            s1[3] = AesEnc(s1[3], RC[15]);
             Mix512(s1, s2);
 
-            s1[0] = aesEnc(s2[0], RC[16]);
-            s1[1] = aesEnc(s2[1], RC[17]);
-            s1[2] = aesEnc(s2[2], RC[18]);
-            s1[3] = aesEnc(s2[3], RC[19]);
-            s1[0] = aesEnc(s1[0], RC[20]);
-            s1[1] = aesEnc(s1[1], RC[21]);
-            s1[2] = aesEnc(s1[2], RC[22]);
-            s1[3] = aesEnc(s1[3], RC[23]);
+            s1[0] = AesEnc(s2[0], RC[16]);
+            s1[1] = AesEnc(s2[1], RC[17]);
+            s1[2] = AesEnc(s2[2], RC[18]);
+            s1[3] = AesEnc(s2[3], RC[19]);
+            s1[0] = AesEnc(s1[0], RC[20]);
+            s1[1] = AesEnc(s1[1], RC[21]);
+            s1[2] = AesEnc(s1[2], RC[22]);
+            s1[3] = AesEnc(s1[3], RC[23]);
             Mix512(s1, s2);
 
-            s1[0] = aesEnc(s2[0], RC[24]);
-            s1[1] = aesEnc(s2[1], RC[25]);
-            s1[2] = aesEnc(s2[2], RC[26]);
-            s1[3] = aesEnc(s2[3], RC[27]);
-            s1[0] = aesEnc(s1[0], RC[28]);
-            s1[1] = aesEnc(s1[1], RC[29]);
-            s1[2] = aesEnc(s1[2], RC[30]);
-            s1[3] = aesEnc(s1[3], RC[31]);
+            s1[0] = AesEnc(s2[0], RC[24]);
+            s1[1] = AesEnc(s2[1], RC[25]);
+            s1[2] = AesEnc(s2[2], RC[26]);
+            s1[3] = AesEnc(s2[3], RC[27]);
+            s1[0] = AesEnc(s1[0], RC[28]);
+            s1[1] = AesEnc(s1[1], RC[29]);
+            s1[2] = AesEnc(s1[2], RC[30]);
+            s1[3] = AesEnc(s1[3], RC[31]);
             Mix512(s1, s2);
 
-            s1[0] = aesEnc(s2[0], RC[32]);
-            s1[1] = aesEnc(s2[1], RC[33]);
-            s1[2] = aesEnc(s2[2], RC[34]);
-            s1[3] = aesEnc(s2[3], RC[35]);
-            s1[0] = aesEnc(s1[0], RC[36]);
-            s1[1] = aesEnc(s1[1], RC[37]);
-            s1[2] = aesEnc(s1[2], RC[38]);
-            s1[3] = aesEnc(s1[3], RC[39]);
+            s1[0] = AesEnc(s2[0], RC[32]);
+            s1[1] = AesEnc(s2[1], RC[33]);
+            s1[2] = AesEnc(s2[2], RC[34]);
+            s1[3] = AesEnc(s2[3], RC[35]);
+            s1[0] = AesEnc(s1[0], RC[36]);
+            s1[1] = AesEnc(s1[1], RC[37]);
+            s1[2] = AesEnc(s1[2], RC[38]);
+            s1[3] = AesEnc(s1[3], RC[39]);
             Mix512(s1, s2);
 
-            s1[0] = Xor(s2[0], msg, 0);
-            s1[1] = Xor(s2[1], msg, 16);
-            s1[2] = Xor(s2[2], msg, 32);
-            s1[3] = Xor(s2[3], msg, 48);
+            Xor(s2[0], msg, s1[0]);
+            Xor(s2[1], msg[16..], s1[1]);
+            Xor(s2[2], msg[32..], s1[2]);
+            Xor(s2[3], msg[48..], s1[3]);
 
-            Array.Copy(s1[0], 8, output, outOff, 8);
-            Array.Copy(s1[1], 8, output, outOff + 8, 8);
-            Array.Copy(s1[2], 0, output, outOff + 16, 8);
-            Array.Copy(s1[3], 0, output, outOff + 24, 8);
+            s1[0].AsSpan(8, 8).CopyTo(output);
+            s1[1].AsSpan(8, 8).CopyTo(output[8..]);
+            s1[2].AsSpan(0, 8).CopyTo(output[16..]);
+            s1[3].AsSpan(0, 8).CopyTo(output[24..]);
 
             return DIGEST_SIZE;
         }
-
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        private int Haraka512256(byte[] msg, Span<byte> output)
+#else
+        private static int Haraka512256(byte[] msg, byte[] output, int outOff)
         {
             byte[][] s1 = new byte[4][];
             s1[0] = new byte[16];
@@ -183,61 +183,59 @@ namespace Org.BouncyCastle.Crypto.Digests
             s2[2] = new byte[16];
             s2[3] = new byte[16];
 
-            //-- Unrolled version of above.
-
             Array.Copy(msg, 0, s1[0], 0, 16);
             Array.Copy(msg, 16, s1[1], 0, 16);
             Array.Copy(msg, 32, s1[2], 0, 16);
             Array.Copy(msg, 48, s1[3], 0, 16);
 
-            s1[0] = aesEnc(s1[0], RC[0]);
-            s1[1] = aesEnc(s1[1], RC[1]);
-            s1[2] = aesEnc(s1[2], RC[2]);
-            s1[3] = aesEnc(s1[3], RC[3]);
-            s1[0] = aesEnc(s1[0], RC[4]);
-            s1[1] = aesEnc(s1[1], RC[5]);
-            s1[2] = aesEnc(s1[2], RC[6]);
-            s1[3] = aesEnc(s1[3], RC[7]);
+            s1[0] = AesEnc(s1[0], RC[0]);
+            s1[1] = AesEnc(s1[1], RC[1]);
+            s1[2] = AesEnc(s1[2], RC[2]);
+            s1[3] = AesEnc(s1[3], RC[3]);
+            s1[0] = AesEnc(s1[0], RC[4]);
+            s1[1] = AesEnc(s1[1], RC[5]);
+            s1[2] = AesEnc(s1[2], RC[6]);
+            s1[3] = AesEnc(s1[3], RC[7]);
             Mix512(s1, s2);
 
-            s1[0] = aesEnc(s2[0], RC[8]);
-            s1[1] = aesEnc(s2[1], RC[9]);
-            s1[2] = aesEnc(s2[2], RC[10]);
-            s1[3] = aesEnc(s2[3], RC[11]);
-            s1[0] = aesEnc(s1[0], RC[12]);
-            s1[1] = aesEnc(s1[1], RC[13]);
-            s1[2] = aesEnc(s1[2], RC[14]);
-            s1[3] = aesEnc(s1[3], RC[15]);
+            s1[0] = AesEnc(s2[0], RC[8]);
+            s1[1] = AesEnc(s2[1], RC[9]);
+            s1[2] = AesEnc(s2[2], RC[10]);
+            s1[3] = AesEnc(s2[3], RC[11]);
+            s1[0] = AesEnc(s1[0], RC[12]);
+            s1[1] = AesEnc(s1[1], RC[13]);
+            s1[2] = AesEnc(s1[2], RC[14]);
+            s1[3] = AesEnc(s1[3], RC[15]);
             Mix512(s1, s2);
 
-            s1[0] = aesEnc(s2[0], RC[16]);
-            s1[1] = aesEnc(s2[1], RC[17]);
-            s1[2] = aesEnc(s2[2], RC[18]);
-            s1[3] = aesEnc(s2[3], RC[19]);
-            s1[0] = aesEnc(s1[0], RC[20]);
-            s1[1] = aesEnc(s1[1], RC[21]);
-            s1[2] = aesEnc(s1[2], RC[22]);
-            s1[3] = aesEnc(s1[3], RC[23]);
+            s1[0] = AesEnc(s2[0], RC[16]);
+            s1[1] = AesEnc(s2[1], RC[17]);
+            s1[2] = AesEnc(s2[2], RC[18]);
+            s1[3] = AesEnc(s2[3], RC[19]);
+            s1[0] = AesEnc(s1[0], RC[20]);
+            s1[1] = AesEnc(s1[1], RC[21]);
+            s1[2] = AesEnc(s1[2], RC[22]);
+            s1[3] = AesEnc(s1[3], RC[23]);
             Mix512(s1, s2);
 
-            s1[0] = aesEnc(s2[0], RC[24]);
-            s1[1] = aesEnc(s2[1], RC[25]);
-            s1[2] = aesEnc(s2[2], RC[26]);
-            s1[3] = aesEnc(s2[3], RC[27]);
-            s1[0] = aesEnc(s1[0], RC[28]);
-            s1[1] = aesEnc(s1[1], RC[29]);
-            s1[2] = aesEnc(s1[2], RC[30]);
-            s1[3] = aesEnc(s1[3], RC[31]);
+            s1[0] = AesEnc(s2[0], RC[24]);
+            s1[1] = AesEnc(s2[1], RC[25]);
+            s1[2] = AesEnc(s2[2], RC[26]);
+            s1[3] = AesEnc(s2[3], RC[27]);
+            s1[0] = AesEnc(s1[0], RC[28]);
+            s1[1] = AesEnc(s1[1], RC[29]);
+            s1[2] = AesEnc(s1[2], RC[30]);
+            s1[3] = AesEnc(s1[3], RC[31]);
             Mix512(s1, s2);
 
-            s1[0] = aesEnc(s2[0], RC[32]);
-            s1[1] = aesEnc(s2[1], RC[33]);
-            s1[2] = aesEnc(s2[2], RC[34]);
-            s1[3] = aesEnc(s2[3], RC[35]);
-            s1[0] = aesEnc(s1[0], RC[36]);
-            s1[1] = aesEnc(s1[1], RC[37]);
-            s1[2] = aesEnc(s1[2], RC[38]);
-            s1[3] = aesEnc(s1[3], RC[39]);
+            s1[0] = AesEnc(s2[0], RC[32]);
+            s1[1] = AesEnc(s2[1], RC[33]);
+            s1[2] = AesEnc(s2[2], RC[34]);
+            s1[3] = AesEnc(s2[3], RC[35]);
+            s1[0] = AesEnc(s1[0], RC[36]);
+            s1[1] = AesEnc(s1[1], RC[37]);
+            s1[2] = AesEnc(s1[2], RC[38]);
+            s1[3] = AesEnc(s1[3], RC[39]);
             Mix512(s1, s2);
 
             s1[0] = Xor(s2[0], msg, 0);
@@ -245,95 +243,36 @@ namespace Org.BouncyCastle.Crypto.Digests
             s1[2] = Xor(s2[2], msg, 32);
             s1[3] = Xor(s2[3], msg, 48);
 
-            s1[0].AsSpan(8, 8).CopyTo(output);
-            s1[1].AsSpan(8, 8).CopyTo(output[8..]);
-            s1[2].AsSpan(0, 8).CopyTo(output[16..]);
-            s1[3].AsSpan(0, 8).CopyTo(output[24..]);
+            Array.Copy(s1[0], 8, output, outOff, 8);
+            Array.Copy(s1[1], 8, output, outOff + 8, 8);
+            Array.Copy(s1[2], 0, output, outOff + 16, 8);
+            Array.Copy(s1[3], 0, output, outOff + 24, 8);
 
             return DIGEST_SIZE;
         }
 #endif
 
-        public override string AlgorithmName => "Haraka-512";
-
-        public override void Update(byte input)
-        {
-            if (off + 1 > 64)
-            {
-                throw new ArgumentException("total input cannot be more than 64 bytes");
-            }
-
-            buffer[off++] = input;
-        }
-
-        public override void BlockUpdate(byte[] input, int inOff, int len)
-        {
-            if (off + len > 64)
-            {
-                throw new ArgumentException("total input cannot be more than 64 bytes");
-            }
-
-            Array.Copy(input, inOff, buffer, off, len);
-            off += len;
-        }
-
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        public override void BlockUpdate(ReadOnlySpan<byte> input)
-        {
-            if (off + input.Length > 64)
-            {
-                throw new ArgumentException("total input cannot be more than 64 bytes");
-            }
-
-            input.CopyTo(buffer.AsSpan(off));
-            off += input.Length;
-        }
-#endif
-
-        public override int DoFinal(byte[] output, int outOff)
+        private static void Mix512(byte[][] s1, byte[][] s2)
         {
-            if (off != 64)
-            {
-                throw new ArgumentException("input must be exactly 64 bytes");
-            }
-
-            if (output.Length - outOff < 32)
-            {
-                throw new ArgumentException("output too short to receive digest");
-            }
-
-            int rv = Haraka512256(buffer, output, outOff);
-
-            Reset();
-
-            return rv;
-        }
-
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        public override int DoFinal(Span<byte> output)
-        {
-            if (off != 64)
-            {
-                throw new ArgumentException("input must be exactly 64 bytes");
-            }
-
-            if (output.Length < 32)
-            {
-                throw new ArgumentException("output too short to receive digest");
-            }
-
-            int rv = Haraka512256(buffer, output);
+            Array.Copy(s1[0], 12, s2[0], 0, 4);
+            Array.Copy(s1[2], 12, s2[0], 4, 4);
+            Array.Copy(s1[1], 12, s2[0], 8, 4);
+            Array.Copy(s1[3], 12, s2[0], 12, 4);
 
-            Reset();
+            Array.Copy(s1[2], 0, s2[1], 0, 4);
+            Array.Copy(s1[0], 0, s2[1], 4, 4);
+            Array.Copy(s1[3], 0, s2[1], 8, 4);
+            Array.Copy(s1[1], 0, s2[1], 12, 4);
 
-            return rv;
-        }
-#endif
+            Array.Copy(s1[2], 4, s2[2], 0, 4);
+            Array.Copy(s1[0], 4, s2[2], 4, 4);
+            Array.Copy(s1[3], 4, s2[2], 8, 4);
+            Array.Copy(s1[1], 4, s2[2], 12, 4);
 
-        public override void Reset()
-        {
-            off = 0;
-            Array.Clear(buffer, 0, 64);
+            Array.Copy(s1[0], 8, s2[3], 0, 4);
+            Array.Copy(s1[2], 8, s2[3], 4, 4);
+            Array.Copy(s1[1], 8, s2[3], 8, 4);
+            Array.Copy(s1[3], 8, s2[3], 12, 4);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/src/crypto/digests/HarakaBase.cs b/crypto/src/crypto/digests/HarakaBase.cs
index 1270de35c..37dda2163 100644
--- a/crypto/src/crypto/digests/HarakaBase.cs
+++ b/crypto/src/crypto/digests/HarakaBase.cs
@@ -2,99 +2,125 @@
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
-    public abstract class HarakaBase : IDigest
+    public abstract class HarakaBase
+        : IDigest
     {
-        protected static readonly int DIGEST_SIZE = 32;
-
-        protected static readonly byte[,] S = {
-        {(byte)0x63, (byte)0x7c, (byte)0x77, (byte)0x7b, (byte)0xf2, (byte)0x6b, (byte)0x6f, (byte)0xc5, (byte)0x30, (byte)0x01, (byte)0x67, (byte)0x2b, (byte)0xfe, (byte)0xd7, (byte)0xab, (byte)0x76},
-        {(byte)0xca, (byte)0x82, (byte)0xc9, (byte)0x7d, (byte)0xfa, (byte)0x59, (byte)0x47, (byte)0xf0, (byte)0xad, (byte)0xd4, (byte)0xa2, (byte)0xaf, (byte)0x9c, (byte)0xa4, (byte)0x72, (byte)0xc0},
-        {(byte)0xb7, (byte)0xfd, (byte)0x93, (byte)0x26, (byte)0x36, (byte)0x3f, (byte)0xf7, (byte)0xcc, (byte)0x34, (byte)0xa5, (byte)0xe5, (byte)0xf1, (byte)0x71, (byte)0xd8, (byte)0x31, (byte)0x15},
-        {(byte)0x04, (byte)0xc7, (byte)0x23, (byte)0xc3, (byte)0x18, (byte)0x96, (byte)0x05, (byte)0x9a, (byte)0x07, (byte)0x12, (byte)0x80, (byte)0xe2, (byte)0xeb, (byte)0x27, (byte)0xb2, (byte)0x75},
-        {(byte)0x09, (byte)0x83, (byte)0x2c, (byte)0x1a, (byte)0x1b, (byte)0x6e, (byte)0x5a, (byte)0xa0, (byte)0x52, (byte)0x3b, (byte)0xd6, (byte)0xb3, (byte)0x29, (byte)0xe3, (byte)0x2f, (byte)0x84},
-        {(byte)0x53, (byte)0xd1, (byte)0x00, (byte)0xed, (byte)0x20, (byte)0xfc, (byte)0xb1, (byte)0x5b, (byte)0x6a, (byte)0xcb, (byte)0xbe, (byte)0x39, (byte)0x4a, (byte)0x4c, (byte)0x58, (byte)0xcf},
-        {(byte)0xd0, (byte)0xef, (byte)0xaa, (byte)0xfb, (byte)0x43, (byte)0x4d, (byte)0x33, (byte)0x85, (byte)0x45, (byte)0xf9, (byte)0x02, (byte)0x7f, (byte)0x50, (byte)0x3c, (byte)0x9f, (byte)0xa8},
-        {(byte)0x51, (byte)0xa3, (byte)0x40, (byte)0x8f, (byte)0x92, (byte)0x9d, (byte)0x38, (byte)0xf5, (byte)0xbc, (byte)0xb6, (byte)0xda, (byte)0x21, (byte)0x10, (byte)0xff, (byte)0xf3, (byte)0xd2},
-        {(byte)0xcd, (byte)0x0c, (byte)0x13, (byte)0xec, (byte)0x5f, (byte)0x97, (byte)0x44, (byte)0x17, (byte)0xc4, (byte)0xa7, (byte)0x7e, (byte)0x3d, (byte)0x64, (byte)0x5d, (byte)0x19, (byte)0x73},
-        {(byte)0x60, (byte)0x81, (byte)0x4f, (byte)0xdc, (byte)0x22, (byte)0x2a, (byte)0x90, (byte)0x88, (byte)0x46, (byte)0xee, (byte)0xb8, (byte)0x14, (byte)0xde, (byte)0x5e, (byte)0x0b, (byte)0xdb},
-        {(byte)0xe0, (byte)0x32, (byte)0x3a, (byte)0x0a, (byte)0x49, (byte)0x06, (byte)0x24, (byte)0x5c, (byte)0xc2, (byte)0xd3, (byte)0xac, (byte)0x62, (byte)0x91, (byte)0x95, (byte)0xe4, (byte)0x79},
-        {(byte)0xe7, (byte)0xc8, (byte)0x37, (byte)0x6d, (byte)0x8d, (byte)0xd5, (byte)0x4e, (byte)0xa9, (byte)0x6c, (byte)0x56, (byte)0xf4, (byte)0xea, (byte)0x65, (byte)0x7a, (byte)0xae, (byte)0x08},
-        {(byte)0xba, (byte)0x78, (byte)0x25, (byte)0x2e, (byte)0x1c, (byte)0xa6, (byte)0xb4, (byte)0xc6, (byte)0xe8, (byte)0xdd, (byte)0x74, (byte)0x1f, (byte)0x4b, (byte)0xbd, (byte)0x8b, (byte)0x8a},
-        {(byte)0x70, (byte)0x3e, (byte)0xb5, (byte)0x66, (byte)0x48, (byte)0x03, (byte)0xf6, (byte)0x0e, (byte)0x61, (byte)0x35, (byte)0x57, (byte)0xb9, (byte)0x86, (byte)0xc1, (byte)0x1d, (byte)0x9e},
-        {(byte)0xe1, (byte)0xf8, (byte)0x98, (byte)0x11, (byte)0x69, (byte)0xd9, (byte)0x8e, (byte)0x94, (byte)0x9b, (byte)0x1e, (byte)0x87, (byte)0xe9, (byte)0xce, (byte)0x55, (byte)0x28, (byte)0xdf},
-        {(byte)0x8c, (byte)0xa1, (byte)0x89, (byte)0x0d, (byte)0xbf, (byte)0xe6, (byte)0x42, (byte)0x68, (byte)0x41, (byte)0x99, (byte)0x2d, (byte)0x0f, (byte)0xb0, (byte)0x54, (byte)0xbb, (byte)0x16}};
+        internal static readonly int DIGEST_SIZE = 32;
 
-        public abstract string AlgorithmName { get; }
+        // Haraka round constants
+        internal static readonly byte[][] RC = new byte[][]
+        {
+            new byte[]{ 0x9D, 0x7B, 0x81, 0x75, 0xF0, 0xFE, 0xC5, 0xB2, 0x0A, 0xC0, 0x20, 0xE6, 0x4C, 0x70, 0x84, 0x06 },
+            new byte[]{ 0x17, 0xF7, 0x08, 0x2F, 0xA4, 0x6B, 0x0F, 0x64, 0x6B, 0xA0, 0xF3, 0x88, 0xE1, 0xB4, 0x66, 0x8B },
+            new byte[]{ 0x14, 0x91, 0x02, 0x9F, 0x60, 0x9D, 0x02, 0xCF, 0x98, 0x84, 0xF2, 0x53, 0x2D, 0xDE, 0x02, 0x34 },
+            new byte[]{ 0x79, 0x4F, 0x5B, 0xFD, 0xAF, 0xBC, 0xF3, 0xBB, 0x08, 0x4F, 0x7B, 0x2E, 0xE6, 0xEA, 0xD6, 0x0E },
+            new byte[]{ 0x44, 0x70, 0x39, 0xBE, 0x1C, 0xCD, 0xEE, 0x79, 0x8B, 0x44, 0x72, 0x48, 0xCB, 0xB0, 0xCF, 0xCB },
+            new byte[]{ 0x7B, 0x05, 0x8A, 0x2B, 0xED, 0x35, 0x53, 0x8D, 0xB7, 0x32, 0x90, 0x6E, 0xEE, 0xCD, 0xEA, 0x7E },
+            new byte[]{ 0x1B, 0xEF, 0x4F, 0xDA, 0x61, 0x27, 0x41, 0xE2, 0xD0, 0x7C, 0x2E, 0x5E, 0x43, 0x8F, 0xC2, 0x67 },
+            new byte[]{ 0x3B, 0x0B, 0xC7, 0x1F, 0xE2, 0xFD, 0x5F, 0x67, 0x07, 0xCC, 0xCA, 0xAF, 0xB0, 0xD9, 0x24, 0x29 },
+            new byte[]{ 0xEE, 0x65, 0xD4, 0xB9, 0xCA, 0x8F, 0xDB, 0xEC, 0xE9, 0x7F, 0x86, 0xE6, 0xF1, 0x63, 0x4D, 0xAB },
+            new byte[]{ 0x33, 0x7E, 0x03, 0xAD, 0x4F, 0x40, 0x2A, 0x5B, 0x64, 0xCD, 0xB7, 0xD4, 0x84, 0xBF, 0x30, 0x1C },
+            new byte[]{ 0x00, 0x98, 0xF6, 0x8D, 0x2E, 0x8B, 0x02, 0x69, 0xBF, 0x23, 0x17, 0x94, 0xB9, 0x0B, 0xCC, 0xB2 },
+            new byte[]{ 0x8A, 0x2D, 0x9D, 0x5C, 0xC8, 0x9E, 0xAA, 0x4A, 0x72, 0x55, 0x6F, 0xDE, 0xA6, 0x78, 0x04, 0xFA },
+            new byte[]{ 0xD4, 0x9F, 0x12, 0x29, 0x2E, 0x4F, 0xFA, 0x0E, 0x12, 0x2A, 0x77, 0x6B, 0x2B, 0x9F, 0xB4, 0xDF },
+            new byte[]{ 0xEE, 0x12, 0x6A, 0xBB, 0xAE, 0x11, 0xD6, 0x32, 0x36, 0xA2, 0x49, 0xF4, 0x44, 0x03, 0xA1, 0x1E },
+            new byte[]{ 0xA6, 0xEC, 0xA8, 0x9C, 0xC9, 0x00, 0x96, 0x5F, 0x84, 0x00, 0x05, 0x4B, 0x88, 0x49, 0x04, 0xAF },
+            new byte[]{ 0xEC, 0x93, 0xE5, 0x27, 0xE3, 0xC7, 0xA2, 0x78, 0x4F, 0x9C, 0x19, 0x9D, 0xD8, 0x5E, 0x02, 0x21 },
+            new byte[]{ 0x73, 0x01, 0xD4, 0x82, 0xCD, 0x2E, 0x28, 0xB9, 0xB7, 0xC9, 0x59, 0xA7, 0xF8, 0xAA, 0x3A, 0xBF },
+            new byte[]{ 0x6B, 0x7D, 0x30, 0x10, 0xD9, 0xEF, 0xF2, 0x37, 0x17, 0xB0, 0x86, 0x61, 0x0D, 0x70, 0x60, 0x62 },
+            new byte[]{ 0xC6, 0x9A, 0xFC, 0xF6, 0x53, 0x91, 0xC2, 0x81, 0x43, 0x04, 0x30, 0x21, 0xC2, 0x45, 0xCA, 0x5A },
+            new byte[]{ 0x3A, 0x94, 0xD1, 0x36, 0xE8, 0x92, 0xAF, 0x2C, 0xBB, 0x68, 0x6B, 0x22, 0x3C, 0x97, 0x23, 0x92 },
+            new byte[]{ 0xB4, 0x71, 0x10, 0xE5, 0x58, 0xB9, 0xBA, 0x6C, 0xEB, 0x86, 0x58, 0x22, 0x38, 0x92, 0xBF, 0xD3 },
+            new byte[]{ 0x8D, 0x12, 0xE1, 0x24, 0xDD, 0xFD, 0x3D, 0x93, 0x77, 0xC6, 0xF0, 0xAE, 0xE5, 0x3C, 0x86, 0xDB },
+            new byte[]{ 0xB1, 0x12, 0x22, 0xCB, 0xE3, 0x8D, 0xE4, 0x83, 0x9C, 0xA0, 0xEB, 0xFF, 0x68, 0x62, 0x60, 0xBB },
+            new byte[]{ 0x7D, 0xF7, 0x2B, 0xC7, 0x4E, 0x1A, 0xB9, 0x2D, 0x9C, 0xD1, 0xE4, 0xE2, 0xDC, 0xD3, 0x4B, 0x73 },
+            new byte[]{ 0x4E, 0x92, 0xB3, 0x2C, 0xC4, 0x15, 0x14, 0x4B, 0x43, 0x1B, 0x30, 0x61, 0xC3, 0x47, 0xBB, 0x43 },
+            new byte[]{ 0x99, 0x68, 0xEB, 0x16, 0xDD, 0x31, 0xB2, 0x03, 0xF6, 0xEF, 0x07, 0xE7, 0xA8, 0x75, 0xA7, 0xDB },
+            new byte[]{ 0x2C, 0x47, 0xCA, 0x7E, 0x02, 0x23, 0x5E, 0x8E, 0x77, 0x59, 0x75, 0x3C, 0x4B, 0x61, 0xF3, 0x6D },
+            new byte[]{ 0xF9, 0x17, 0x86, 0xB8, 0xB9, 0xE5, 0x1B, 0x6D, 0x77, 0x7D, 0xDE, 0xD6, 0x17, 0x5A, 0xA7, 0xCD },
+            new byte[]{ 0x5D, 0xEE, 0x46, 0xA9, 0x9D, 0x06, 0x6C, 0x9D, 0xAA, 0xE9, 0xA8, 0x6B, 0xF0, 0x43, 0x6B, 0xEC },
+            new byte[]{ 0xC1, 0x27, 0xF3, 0x3B, 0x59, 0x11, 0x53, 0xA2, 0x2B, 0x33, 0x57, 0xF9, 0x50, 0x69, 0x1E, 0xCB },
+            new byte[]{ 0xD9, 0xD0, 0x0E, 0x60, 0x53, 0x03, 0xED, 0xE4, 0x9C, 0x61, 0xDA, 0x00, 0x75, 0x0C, 0xEE, 0x2C },
+            new byte[]{ 0x50, 0xA3, 0xA4, 0x63, 0xBC, 0xBA, 0xBB, 0x80, 0xAB, 0x0C, 0xE9, 0x96, 0xA1, 0xA5, 0xB1, 0xF0 },
+            new byte[]{ 0x39, 0xCA, 0x8D, 0x93, 0x30, 0xDE, 0x0D, 0xAB, 0x88, 0x29, 0x96, 0x5E, 0x02, 0xB1, 0x3D, 0xAE },
+            new byte[]{ 0x42, 0xB4, 0x75, 0x2E, 0xA8, 0xF3, 0x14, 0x88, 0x0B, 0xA4, 0x54, 0xD5, 0x38, 0x8F, 0xBB, 0x17 },
+            new byte[]{ 0xF6, 0x16, 0x0A, 0x36, 0x79, 0xB7, 0xB6, 0xAE, 0xD7, 0x7F, 0x42, 0x5F, 0x5B, 0x8A, 0xBB, 0x34 },
+            new byte[]{ 0xDE, 0xAF, 0xBA, 0xFF, 0x18, 0x59, 0xCE, 0x43, 0x38, 0x54, 0xE5, 0xCB, 0x41, 0x52, 0xF6, 0x26 },
+            new byte[]{ 0x78, 0xC9, 0x9E, 0x83, 0xF7, 0x9C, 0xCA, 0xA2, 0x6A, 0x02, 0xF3, 0xB9, 0x54, 0x9A, 0xE9, 0x4C },
+            new byte[]{ 0x35, 0x12, 0x90, 0x22, 0x28, 0x6E, 0xC0, 0x40, 0xBE, 0xF7, 0xDF, 0x1B, 0x1A, 0xA5, 0x51, 0xAE },
+            new byte[]{ 0xCF, 0x59, 0xA6, 0x48, 0x0F, 0xBC, 0x73, 0xC1, 0x2B, 0xD2, 0x7E, 0xBA, 0x3C, 0x61, 0xC1, 0xA0 },
+            new byte[]{ 0xA1, 0x9D, 0xC5, 0xE9, 0xFD, 0xBD, 0xD6, 0x4A, 0x88, 0x82, 0x28, 0x02, 0x03, 0xCC, 0x6A, 0x75 },
+        };
 
-        static byte sBox(byte x)
+        private static readonly byte[,] S =
         {
-            return S[(uint)(((x & 0xFF) >> 4)),x & 0xF];
+            { 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76 },
+            { 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0 },
+            { 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15 },
+            { 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75 },
+            { 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84 },
+            { 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF },
+            { 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8 },
+            { 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2 },
+            { 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73 },
+            { 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB },
+            { 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79 },
+            { 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08 },
+            { 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A },
+            { 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E },
+            { 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF },
+            { 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 },
+        };
+
+        private static byte SBox(byte x)
+        {
+            return S[(uint)x >> 4, x & 0xFU];
         }
 
-        static byte[] subBytes(byte[] s)
+        private static byte[] SubBytes(byte[] s)
         {
             byte[] output = new byte[s.Length];
             for(int i = 0; i < 16; ++i)
             {
-                output[i] = sBox(s[i]);
+                output[i] = SBox(s[i]);
             }
             return output;
         }
 
-        static byte[] shiftRows(byte[] s)
+        private static byte[] ShiftRows(byte[] s)
         {
             return new byte[]{
-            s[0], s[5], s[10], s[15],
-            s[4], s[9], s[14], s[3],
-            s[8], s[13], s[2], s[7],
-            s[12], s[1], s[6], s[11]
-        };
+                s[0], s[5], s[10], s[15],
+                s[4], s[9], s[14], s[3],
+                s[8], s[13], s[2], s[7],
+                s[12], s[1], s[6], s[11]
+            };
         }
 
-        protected static byte[] aesEnc(byte[] s, byte[] rk)
+        internal static byte[] AesEnc(byte[] s, byte[] rk)
         {
-            s = subBytes(s);
-            s = shiftRows(s);
-            s = mixColumns(s);
-            xorReverse(s, rk);
+            s = SubBytes(s);
+            s = ShiftRows(s);
+            s = MixColumns(s);
+            XorWith(rk, s);
             return s;
         }
 
-        static byte xTime(byte x)
+        private static byte MulX(byte p)
         {
-            if ((x >> 7) > 0)
-            {
-                return (byte)(((x << 1) ^ 0x1b) & 0xff);
-            }
-            else
-            {
-                return (byte)((x << 1) & 0xff);
-            }
+            return (byte)(((p & 0x7F) << 1) ^ (((uint)p >> 7) * 0x1BU));
         }
 
-
-        static void xorReverse(byte[] x, byte[] y)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal static void Xor(ReadOnlySpan<byte> x, ReadOnlySpan<byte> y, Span<byte> z)
         {
-            x[0] = (byte)(x[0] ^ y[15]);
-            x[1] = (byte)(x[1] ^ y[14]);
-            x[2] = (byte)(x[2] ^ y[13]);
-            x[3] = (byte)(x[3] ^ y[12]);
-            x[4] = (byte)(x[4] ^ y[11]);
-            x[5] = (byte)(x[5] ^ y[10]);
-            x[6] = (byte)(x[6] ^ y[9]);
-            x[7] = (byte)(x[7] ^ y[8]);
-            x[8] = (byte)(x[8] ^ y[7]);
-            x[9] = (byte)(x[9] ^ y[6]);
-            x[10] = (byte)(x[10] ^ y[5]);
-            x[11] = (byte)(x[11] ^ y[4]);
-            x[12] = (byte)(x[12] ^ y[3]);
-            x[13] = (byte)(x[13] ^ y[2]);
-            x[14] = (byte)(x[14] ^ y[1]);
-            x[15] = (byte)(x[15] ^ y[0]);
+            for (int i = 0; i < z.Length; i++)
+            {
+                z[i] = (byte)(x[i] ^ y[i]);
+            }
         }
-
-
-        protected static byte[] Xor(byte[] x, byte[] y, int yStart)
+#else
+        internal static byte[] Xor(byte[] x, byte[] y, int yStart)
         {
             byte[] output = new byte[16];
             for (int i = 0; i < output.Length; i++)
@@ -103,33 +129,40 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
             return output;
         }
+#endif
 
+        private static void XorWith(byte[] x, byte[] z)
+        {
+            for (int i = 0; i < 16; ++i)
+            {
+                z[i] ^= x[i];
+            }
+        }
 
-        static private byte[] mixColumns(byte[] s)
+        private static byte[] MixColumns(byte[] s)
         {
             byte[] output = new byte[s.Length];
             int j = 0, i4;
             for (int i = 0; i < 4; i++)
             {
                 i4 = i << 2;
-                output[j++] = (byte)(xTime(s[i4]) ^ xTime(s[i4 + 1]) ^ s[i4 + 1] ^ s[i4 + 2] ^ s[i4 + 3]);
-                output[j++] = (byte)(s[i4] ^ xTime(s[i4 + 1]) ^ xTime(s[i4 + 2]) ^ s[i4 + 2] ^ s[i4 + 3]);
-                output[j++] = (byte)(s[i4] ^ s[i4 + 1] ^ xTime(s[i4 + 2]) ^ xTime(s[i4 + 3]) ^ s[i4 + 3]);
-                output[j++] = (byte)(xTime(s[i4]) ^ s[i4] ^ s[i4 + 1] ^ s[i4 + 2] ^ xTime(s[i4 + 3]));
+                output[j++] = (byte)(MulX(s[i4]) ^ MulX(s[i4 + 1]) ^ s[i4 + 1] ^ s[i4 + 2] ^ s[i4 + 3]);
+                output[j++] = (byte)(s[i4] ^ MulX(s[i4 + 1]) ^ MulX(s[i4 + 2]) ^ s[i4 + 2] ^ s[i4 + 3]);
+                output[j++] = (byte)(s[i4] ^ s[i4 + 1] ^ MulX(s[i4 + 2]) ^ MulX(s[i4 + 3]) ^ s[i4 + 3]);
+                output[j++] = (byte)(MulX(s[i4]) ^ s[i4] ^ s[i4 + 1] ^ s[i4 + 2] ^ MulX(s[i4 + 3]));
             }
 
             return output;
         }
 
+        public abstract string AlgorithmName { get; }
+
         public int GetDigestSize()
         {
             return DIGEST_SIZE;
         }
 
-        public int GetByteLength()
-        {
-            throw new NotImplementedException();
-        }
+        public abstract int GetByteLength();
 
         public abstract void Update(byte input);