summary refs log tree commit diff
path: root/crypto/src
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2023-02-13 00:34:55 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2023-02-13 00:34:55 +0700
commitf52b9a1ea4b3b1aea3615636d282b119a2e7a05e (patch)
tree7b41c49bc5cdd3af6f12e8fd33896c5d831601e6 /crypto/src
parentSupport explicit blockSize (diff)
downloadBouncyCastle.NET-ed25519-f52b9a1ea4b3b1aea3615636d282b119a2e7a05e.tar.xz
Fix/refactor LWC algorithms
Diffstat (limited to 'crypto/src')
-rw-r--r--crypto/src/crypto/digests/AsconDigest.cs385
-rw-r--r--crypto/src/crypto/digests/ISAPDigest.cs184
-rw-r--r--crypto/src/crypto/digests/PhotonBeetleDigest.cs14
-rw-r--r--crypto/src/crypto/digests/SparkleDigest.cs75
-rw-r--r--crypto/src/crypto/engines/AsconEngine.cs10
-rw-r--r--crypto/src/crypto/engines/ElephantEngine.cs131
-rw-r--r--crypto/src/crypto/engines/ISAPEngine.cs12
-rw-r--r--crypto/src/crypto/engines/PhotonBeetleEngine.cs44
-rw-r--r--crypto/src/crypto/engines/SparkleEngine.cs25
-rw-r--r--crypto/src/crypto/engines/XoodyakEngine.cs42
10 files changed, 451 insertions, 471 deletions
diff --git a/crypto/src/crypto/digests/AsconDigest.cs b/crypto/src/crypto/digests/AsconDigest.cs
index 2607cddde..343036b99 100644
--- a/crypto/src/crypto/digests/AsconDigest.cs
+++ b/crypto/src/crypto/digests/AsconDigest.cs
@@ -1,10 +1,15 @@
 using System;
-using System.IO;
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+using System.Runtime.CompilerServices;
+#endif
+
 using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
-    public class AsconDigest : IDigest
+    public sealed class AsconDigest
+        : IDigest
     {
         public enum AsconParameters
         {
@@ -14,236 +19,266 @@ namespace Org.BouncyCastle.Crypto.Digests
             AsconXofA,
         }
 
-        public AsconDigest(AsconParameters parameters)
-        {
-            asconParameters = parameters;
-            switch (parameters)
-            {
-                case AsconParameters.AsconHash:
-                    ASCON_PB_ROUNDS = 12;
-                    algorithmName = "Ascon-Hash";
-                    break;
-                case AsconParameters.AsconHashA:
-                    ASCON_PB_ROUNDS = 8;
-                    algorithmName = "Ascon-HashA";
-                    break;
-                case AsconParameters.AsconXof:
-                    ASCON_PB_ROUNDS = 12;
-                    algorithmName = "Ascon-Xof";
-                    break;
-                case AsconParameters.AsconXofA:
-                    ASCON_PB_ROUNDS = 8;
-                    algorithmName = "Ascon-XofA";
-                    break;
-                default:
-                    throw new ArgumentException("Invalid parameter settings for Ascon Hash");
-            }
-            Reset();
-        }
-
-        private AsconParameters asconParameters;
+        private readonly AsconParameters m_asconParameters;
+        private readonly int ASCON_PB_ROUNDS;
 
-        private string algorithmName;
-
-        private readonly MemoryStream buffer = new MemoryStream();
         private ulong x0;
         private ulong x1;
         private ulong x2;
         private ulong x3;
         private ulong x4;
-        private readonly int CRYPTO_BYTES = 32;
-        private int ASCON_PB_ROUNDS;
-
-        public string AlgorithmName => algorithmName;
 
-        private ulong ROR(ulong x, int n)
-        {
-            return x >> n | x << (64 - n);
-        }
+        private readonly byte[] m_buf = new byte[8];
+        private int m_bufPos = 0;
 
-        private void ROUND(ulong C)
+        public AsconDigest(AsconParameters parameters)
         {
-            ulong t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C));
-            ulong t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3));
-            ulong t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4);
-            ulong t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4));
-            ulong t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1);
-            x0 = t0 ^ ROR(t0, 19) ^ ROR(t0, 28);
-            x1 = t1 ^ ROR(t1, 39) ^ ROR(t1, 61);
-            x2 = ~(t2 ^ ROR(t2, 1) ^ ROR(t2, 6));
-            x3 = t3 ^ ROR(t3, 10) ^ ROR(t3, 17);
-            x4 = t4 ^ ROR(t4, 7) ^ ROR(t4, 41);
+            m_asconParameters = parameters;
+            switch (parameters)
+            {
+            case AsconParameters.AsconHash:
+                ASCON_PB_ROUNDS = 12;
+                break;
+            case AsconParameters.AsconHashA:
+                ASCON_PB_ROUNDS = 8;
+                break;
+            case AsconParameters.AsconXof:
+                ASCON_PB_ROUNDS = 12;
+                break;
+            case AsconParameters.AsconXofA:
+                ASCON_PB_ROUNDS = 8;
+                break;
+            default:
+                throw new ArgumentException("Invalid parameter settings for Ascon Hash");
+            }
+            Reset();
         }
 
-        private void P(int nr)
+        public string AlgorithmName
         {
-            if (nr == 12)
+            get
             {
-                ROUND(0xf0UL);
-                ROUND(0xe1UL);
-                ROUND(0xd2UL);
-                ROUND(0xc3UL);
+                switch (m_asconParameters)
+                {
+                case AsconParameters.AsconHash:     return "Ascon-Hash";
+                case AsconParameters.AsconHashA:    return "Ascon-HashA";
+                case AsconParameters.AsconXof:      return "Ascon-Xof";
+                case AsconParameters.AsconXofA:     return "Ascon-XofA";
+                default: throw new InvalidOperationException();
+                }
             }
-            if (nr >= 8)
-            {
-                ROUND(0xb4UL);
-                ROUND(0xa5UL);
-            }
-            ROUND(0x96UL);
-            ROUND(0x87UL);
-            ROUND(0x78UL);
-            ROUND(0x69UL);
-            ROUND(0x5aUL);
-            ROUND(0x4bUL);
         }
 
-        private ulong PAD(int i)
-        {
-            return 0x80UL << (56 - (i << 3));
-        }
+        public int GetDigestSize() => 32;
+
+        public int GetByteLength() => 8;
 
-        private ulong LOADBYTES(byte[] bytes, int inOff, int n)
+        public void Update(byte input)
         {
-            ulong x = 0;
-            for (int i = 0; i < n; ++i)
+            m_buf[m_bufPos] = input;
+            if (++m_bufPos == 8)
             {
-                x |= (bytes[i + inOff] & 0xFFUL) << ((7 - i) << 3);
+                x0 ^= Pack.BE_To_UInt64(m_buf, 0);
+                P(ASCON_PB_ROUNDS);
+                m_bufPos = 0;
             }
-            return x;
         }
 
-        private void STOREBYTES(byte[] bytes, int inOff, ulong w, int n)
+        public void BlockUpdate(byte[] input, int inOff, int inLen)
         {
-            for (int i = 0; i < n; ++i)
+            Check.DataLength(input, inOff, inLen, "input buffer too short");
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            BlockUpdate(input.AsSpan(inOff, inLen));
+#else
+            if (inLen < 1)
+                return;
+
+            int available = 8 - m_bufPos;
+            if (inLen < available)
             {
-                bytes[i + inOff] = (byte)(w >> ((7 - i) << 3));
+                Array.Copy(input, inOff, m_buf, m_bufPos, inLen);
+                m_bufPos += inLen;
+                return;
             }
-        }
 
-        public int GetDigestSize()
-        {
-            return CRYPTO_BYTES;
-        }
+            int inPos = 0;
+            if (m_bufPos > 0)
+            {
+                Array.Copy(input, inOff, m_buf, m_bufPos, available);
+                inPos += available;
+                x0 ^= Pack.BE_To_UInt64(m_buf, 0);
+                P(ASCON_PB_ROUNDS);
+            }
 
+            int remaining;
+            while ((remaining = inLen - inPos) >= 8)
+            {
+                x0 ^= Pack.BE_To_UInt64(input, inOff + inPos);
+                P(ASCON_PB_ROUNDS);
+                inPos += 8;
+            }
 
-        public void Update(byte input)
-        {
-            buffer.Write(new byte[] { input }, 0, 1);
+            Array.Copy(input, inOff + inPos, m_buf, 0, remaining);
+            m_bufPos = remaining;
+#endif
         }
 
-
-        public void BlockUpdate(byte[] input, int inOff, int len)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
         {
-            if ((inOff + len) > input.Length)
+            int available = 8 - m_bufPos;
+            if (input.Length < available)
             {
-                throw new DataLengthException("input buffer too ushort");
+                input.CopyTo(m_buf.AsSpan(m_bufPos));
+                m_bufPos += input.Length;
+                return;
             }
-            buffer.Write(input, inOff, len);
-        }
-
 
-        public int DoFinal(byte[] output, int outOff)
-        {
-            if (CRYPTO_BYTES + outOff > output.Length)
+            if (m_bufPos > 0)
             {
-                throw new OutputLengthException("output buffer is too ushort");
+                input[..available].CopyTo(m_buf.AsSpan(m_bufPos));
+                x0 ^= Pack.BE_To_UInt64(m_buf);
+                P(ASCON_PB_ROUNDS);
+                input = input[available..];
             }
-            byte[] input = buffer.GetBuffer();
-            int len = (int)buffer.Length;
-            int inOff = 0;
-            ///* initialize */
-            //x0 = ASCON_IV;
-            //x1 = 0;
-            //x2 = 0;
-            //x3 = 0;
-            //x4 = 0;
-            //P(ASCON_PA_ROUNDS);
-            /* absorb full plaintext blocks */
-            int ASCON_HASH_RATE = 8;
-            while (len >= ASCON_HASH_RATE)
+
+            while (input.Length >= 8)
             {
-                x0 ^= LOADBYTES(input, inOff, 8);
+                x0 ^= Pack.BE_To_UInt64(input);
                 P(ASCON_PB_ROUNDS);
-                inOff += ASCON_HASH_RATE;
-                len -= ASCON_HASH_RATE;
+                input = input[8..];
             }
-            /* absorb readonly plaintext block */
-            x0 ^= LOADBYTES(input, inOff, len);
-            x0 ^= PAD(len);
-            int ASCON_PA_ROUNDS = 12;
-            P(ASCON_PA_ROUNDS);
-            /* squeeze full output blocks */
-            len = CRYPTO_BYTES;
-            while (len > ASCON_HASH_RATE)
+
+            input.CopyTo(m_buf);
+            m_bufPos = input.Length;
+        }
+#endif
+
+        public int DoFinal(byte[] output, int outOff)
+        {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return DoFinal(output.AsSpan(outOff));
+#else
+            Check.OutputLength(output, outOff, 32, "output buffer is too short");
+
+            m_buf[m_bufPos] = 0x80;
+            x0 ^= Pack.BE_To_UInt64(m_buf, 0) & (ulong.MaxValue << (56 - (m_bufPos << 3)));
+
+            P(12);
+            Pack.UInt64_To_BE(x0, output, outOff);
+
+            for (int i = 0; i < 3; ++i)
             {
-                STOREBYTES(output, outOff, x0, 8);
+                outOff += 8;
+
                 P(ASCON_PB_ROUNDS);
-                outOff += ASCON_HASH_RATE;
-                len -= ASCON_HASH_RATE;
+                Pack.UInt64_To_BE(x0, output, outOff);
             }
-            /* squeeze readonly output block */
-            STOREBYTES(output, outOff, x0, len);
+
             Reset();
-            return CRYPTO_BYTES;
+            return 32;
+#endif
         }
 
-
-        public void Reset()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int DoFinal(Span<byte> output)
         {
-            buffer.SetLength(0);
-            /* initialize */
-            switch (asconParameters)
+            Check.OutputLength(output, 32, "output buffer is too short");
+
+            m_buf[m_bufPos] = 0x80;
+            x0 ^= Pack.BE_To_UInt64(m_buf) & (ulong.MaxValue << (56 - (m_bufPos << 3)));
+
+            P(12);
+            Pack.UInt64_To_BE(x0, output);
+
+            for (int i = 0; i < 3; ++i)
             {
-                case AsconParameters.AsconHashA:
-                    x0 = 92044056785660070UL;
-                    x1 = 8326807761760157607UL;
-                    x2 = 3371194088139667532UL;
-                    x3 = 15489749720654559101UL;
-                    x4 = 11618234402860862855UL;
-                    break;
-                case AsconParameters.AsconHash:
-                    x0 = 17191252062196199485UL;
-                    x1 = 10066134719181819906UL;
-                    x2 = 13009371945472744034UL;
-                    x3 = 4834782570098516968UL;
-                    x4 = 3787428097924915520UL;
-                    break;
-                case AsconParameters.AsconXof:
-                    x0 = 13077933504456348694UL;
-                    x1 = 3121280575360345120UL;
-                    x2 = 7395939140700676632UL;
-                    x3 = 6533890155656471820UL;
-                    x4 = 5710016986865767350UL;
-                    break;
-                case AsconParameters.AsconXofA:
-                    x0 = 4940560291654768690UL;
-                    x1 = 14811614245468591410UL;
-                    x2 = 17849209150987444521UL;
-                    x3 = 2623493988082852443UL;
-                    x4 = 12162917349548726079UL;
-                    break;
+                output = output[8..];
+
+                P(ASCON_PB_ROUNDS);
+                Pack.UInt64_To_BE(x0, output);
             }
+
+            Reset();
+            return 32;
         }
+#endif
 
-        public int GetByteLength()
+        public void Reset()
         {
-            throw new NotImplementedException();
+            Array.Clear(m_buf, 0, m_buf.Length);
+            m_bufPos = 0;
+
+            switch (m_asconParameters)
+            {
+            case AsconParameters.AsconHashA:
+                x0 = 92044056785660070UL;
+                x1 = 8326807761760157607UL;
+                x2 = 3371194088139667532UL;
+                x3 = 15489749720654559101UL;
+                x4 = 11618234402860862855UL;
+                break;
+            case AsconParameters.AsconHash:
+                x0 = 17191252062196199485UL;
+                x1 = 10066134719181819906UL;
+                x2 = 13009371945472744034UL;
+                x3 = 4834782570098516968UL;
+                x4 = 3787428097924915520UL;
+                break;
+            case AsconParameters.AsconXof:
+                x0 = 13077933504456348694UL;
+                x1 = 3121280575360345120UL;
+                x2 = 7395939140700676632UL;
+                x3 = 6533890155656471820UL;
+                x4 = 5710016986865767350UL;
+                break;
+            case AsconParameters.AsconXofA:
+                x0 = 4940560291654768690UL;
+                x1 = 14811614245468591410UL;
+                x2 = 17849209150987444521UL;
+                x3 = 2623493988082852443UL;
+                x4 = 12162917349548726079UL;
+                break;
+            default:
+                throw new InvalidOperationException();
+            }
         }
 
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        public int DoFinal(Span<byte> output)
+        private void P(int nr)
         {
-            byte[] rv = new byte[32];
-            int rlt = DoFinal(rv, 0);
-            rv.AsSpan(0, 32).CopyTo(output);
-            return rlt;
+            if (nr == 12)
+            {
+                ROUND(0xf0UL);
+                ROUND(0xe1UL);
+                ROUND(0xd2UL);
+                ROUND(0xc3UL);
+            }
+            ROUND(0xb4UL);
+            ROUND(0xa5UL);
+            ROUND(0x96UL);
+            ROUND(0x87UL);
+            ROUND(0x78UL);
+            ROUND(0x69UL);
+            ROUND(0x5aUL);
+            ROUND(0x4bUL);
         }
 
-        public void BlockUpdate(ReadOnlySpan<byte> input)
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+        private void ROUND(ulong C)
         {
-            buffer.Write(input.ToArray(), 0, input.Length);
+            ulong t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C));
+            ulong t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3));
+            ulong t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4);
+            ulong t3 = x0 ^ x1 ^ x2 ^ C ^ (~x0 & (x3 ^ x4));
+            ulong t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1);
+            x0 = t0 ^ Longs.RotateRight(t0, 19) ^ Longs.RotateRight(t0, 28);
+            x1 = t1 ^ Longs.RotateRight(t1, 39) ^ Longs.RotateRight(t1, 61);
+            x2 = ~(t2 ^ Longs.RotateRight(t2, 1) ^ Longs.RotateRight(t2, 6));
+            x3 = t3 ^ Longs.RotateRight(t3, 10) ^ Longs.RotateRight(t3, 17);
+            x4 = t4 ^ Longs.RotateRight(t4, 7) ^ Longs.RotateRight(t4, 41);
         }
-#endif
     }
 }
-
diff --git a/crypto/src/crypto/digests/ISAPDigest.cs b/crypto/src/crypto/digests/ISAPDigest.cs
index 3be28e4e2..348242e6e 100644
--- a/crypto/src/crypto/digests/ISAPDigest.cs
+++ b/crypto/src/crypto/digests/ISAPDigest.cs
@@ -1,149 +1,165 @@
 using System;
 using System.IO;
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+using System.Runtime.CompilerServices;
+#endif
+
 using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Digests
 {
-    public class ISAPDigest : IDigest
+    public sealed class IsapDigest
+        : IDigest
     {
         private ulong x0, x1, x2, x3, x4;
         private ulong t0, t1, t2, t3, t4;
         private MemoryStream buffer = new MemoryStream();
 
-        private void ROUND(ulong C)
-        {
-            t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C));
-            t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3));
-            t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4);
-            t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4));
-            t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1);
-            x0 = t0 ^ ROTR(t0, 19) ^ ROTR(t0, 28);
-            x1 = t1 ^ ROTR(t1, 39) ^ ROTR(t1, 61);
-            x2 = ~(t2 ^ ROTR(t2, 1) ^ ROTR(t2, 6));
-            x3 = t3 ^ ROTR(t3, 10) ^ ROTR(t3, 17);
-            x4 = t4 ^ ROTR(t4, 7) ^ ROTR(t4, 41);
-        }
-
-        private void P12()
-        {
-            ROUND(0xf0);
-            ROUND(0xe1);
-            ROUND(0xd2);
-            ROUND(0xc3);
-            ROUND(0xb4);
-            ROUND(0xa5);
-            ROUND(0x96);
-            ROUND(0x87);
-            ROUND(0x78);
-            ROUND(0x69);
-            ROUND(0x5a);
-            ROUND(0x4b);
-        }
+        public string AlgorithmName => "ISAP Hash";
 
-        private ulong ROTR(ulong x, int n)
-        {
-            return (x >> n) | (x << (64 - n));
-        }
+        public int GetDigestSize() => 32;
 
-        protected ulong U64BIG(ulong x)
-        {
-            return ((ROTR(x, 8) & (0xFF000000FF000000UL)) | (ROTR(x, 24) & (0x00FF000000FF0000UL)) |
-                (ROTR(x, 40) & (0x0000FF000000FF00UL)) | (ROTR(x, 56) & (0x000000FF000000FFUL)));
-        }
+        public int GetByteLength() => 8;
 
-        public string AlgorithmName
+        public void Update(byte input)
         {
-            get { return "ISAP Hash"; }
+            buffer.WriteByte(input);
         }
 
         public void BlockUpdate(byte[] input, int inOff, int inLen)
         {
-            if (inOff + inLen > input.Length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
+            Check.DataLength(input, inOff, inLen, "input buffer too short");
+
             buffer.Write(input, inOff, inLen);
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         public void BlockUpdate(ReadOnlySpan<byte> input)
         {
-            buffer.Write(input.ToArray(), 0, input.Length);
-        }
-
-        public int DoFinal(Span<byte> output)
-        {
-            byte[] rv = new byte[32];
-            int rlt = DoFinal(rv, 0);
-            rv.AsSpan(0, 32).CopyTo(output);
-            return rlt;
+            buffer.Write(input);
         }
-
 #endif
 
         public int DoFinal(byte[] output, int outOff)
         {
-            if (32 + outOff > output.Length)
-            {
-                throw new OutputLengthException("output buffer is too short");
-            }
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return DoFinal(output.AsSpan(outOff));
+#else
+            Check.OutputLength(output, outOff, 32, "output buffer is too short");
+
             t0 = t1 = t2 = t3 = t4 = 0;
+
             /* init state */
             x0 = 17191252062196199485UL;
             x1 = 10066134719181819906UL;
             x2 = 13009371945472744034UL;
             x3 = 4834782570098516968UL;
             x4 = 3787428097924915520UL;
-            /* absorb */
+
             byte[] input = buffer.GetBuffer();
-            int len = (int)buffer.Length;
-            ulong[] in64 = new ulong[len >> 3];
-            Pack.LE_To_UInt64(input, 0, in64, 0, in64.Length);
-            int idx = 0;
+            int len = Convert.ToInt32(buffer.Length);
+
+            int pos = 0;
             while (len >= 8)
             {
-                x0 ^= U64BIG(in64[idx++]);
-                P12();
+                x0 ^= Pack.BE_To_UInt64(input, pos);
+                pos += 8;
                 len -= 8;
+                P12();
             }
-            /* absorb final input block */
             x0 ^= 0x80UL << ((7 - len) << 3);
-            while (len > 0)
+            if (len > 0)
             {
-                x0 ^= (input[(idx << 3) + --len] & 0xFFUL) << ((7 - len) << 3);
+                x0 ^= Pack.BE_To_UInt64_High(input, pos, len);
             }
-            P12();
-            // squeeze
-            ulong[] out64 = new ulong[4];
-            for (idx = 0; idx < 3; ++idx)
+
+            for (int i = 0; i < 4; ++i)
             {
-                out64[idx] = U64BIG(x0);
                 P12();
+                Pack.UInt64_To_BE(x0, output, outOff + (i << 3));
             }
-            /* squeeze final output block */
-            out64[idx] = U64BIG(x0);
-            Pack.UInt64_To_LE(out64, output, outOff);
+
             return 32;
+#endif
         }
 
-        public int GetByteLength()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int DoFinal(Span<byte> output)
         {
-            throw new NotImplementedException();
-        }
+            Check.OutputLength(output, 32, "output buffer is too short");
+
+            t0 = t1 = t2 = t3 = t4 = 0;
+
+            /* init state */
+            x0 = 17191252062196199485UL;
+            x1 = 10066134719181819906UL;
+            x2 = 13009371945472744034UL;
+            x3 = 4834782570098516968UL;
+            x4 = 3787428097924915520UL;
+
+            if (!buffer.TryGetBuffer(out var bufferContents))
+                throw new UnauthorizedAccessException();
+
+            var input = bufferContents.AsSpan();
+            while (input.Length >= 8)
+            {
+                x0 ^= Pack.BE_To_UInt64(input);
+                input = input[8..];
+                P12();
+            }
+            x0 ^= 0x80UL << ((7 - input.Length) << 3);
+            if (!input.IsEmpty)
+            {
+                x0 ^= Pack.BE_To_UInt64_High(input);
+            }
+
+            for (int i = 0; i < 4; ++i)
+            {
+                P12();
+                Pack.UInt64_To_BE(x0, output[(i << 3)..]);
+            }
 
-        public int GetDigestSize()
-        {
             return 32;
         }
+#endif
 
         public void Reset()
         {
             buffer.SetLength(0);
         }
 
-        public void Update(byte input)
+        private void P12()
         {
-            buffer.Write(new byte[] { input }, 0, 1);
+            ROUND(0xf0);
+            ROUND(0xe1);
+            ROUND(0xd2);
+            ROUND(0xc3);
+            ROUND(0xb4);
+            ROUND(0xa5);
+            ROUND(0x96);
+            ROUND(0x87);
+            ROUND(0x78);
+            ROUND(0x69);
+            ROUND(0x5a);
+            ROUND(0x4b);
+        }
+
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+        private void ROUND(ulong C)
+        {
+            t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C));
+            t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3));
+            t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4);
+            t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4));
+            t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1);
+            x0 = t0 ^ Longs.RotateRight(t0, 19) ^ Longs.RotateRight(t0, 28);
+            x1 = t1 ^ Longs.RotateRight(t1, 39) ^ Longs.RotateRight(t1, 61);
+            x2 = ~(t2 ^ Longs.RotateRight(t2, 1) ^ Longs.RotateRight(t2, 6));
+            x3 = t3 ^ Longs.RotateRight(t3, 10) ^ Longs.RotateRight(t3, 17);
+            x4 = t4 ^ Longs.RotateRight(t4, 7) ^ Longs.RotateRight(t4, 41);
         }
     }
 }
diff --git a/crypto/src/crypto/digests/PhotonBeetleDigest.cs b/crypto/src/crypto/digests/PhotonBeetleDigest.cs
index 13b30e025..1af8aee2c 100644
--- a/crypto/src/crypto/digests/PhotonBeetleDigest.cs
+++ b/crypto/src/crypto/digests/PhotonBeetleDigest.cs
@@ -67,19 +67,19 @@ namespace Org.BouncyCastle.Crypto.Digests
 
         public String AlgorithmName => "Photon-Beetle Hash";
 
+        public int GetDigestSize() => TAG_INBYTES;
 
-        public int GetDigestSize()
+        public int GetByteLength()
         {
-            return TAG_INBYTES;
+            // TODO
+            throw new NotImplementedException();
         }
 
-
         public void Update(byte input)
         {
-            buffer.Write(new byte[] { input }, 0, 1);
+            buffer.WriteByte(input);
         }
 
-
         public void BlockUpdate(byte[] input, int inOff, int len)
         {
             if ((inOff + len) > input.Length)
@@ -223,10 +223,6 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
         }
 
-        public int GetByteLength()
-        {
-            throw new NotImplementedException();
-        }
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
 
         public int DoFinal(Span<byte> output)
diff --git a/crypto/src/crypto/digests/SparkleDigest.cs b/crypto/src/crypto/digests/SparkleDigest.cs
index b8134dd91..ad558effc 100644
--- a/crypto/src/crypto/digests/SparkleDigest.cs
+++ b/crypto/src/crypto/digests/SparkleDigest.cs
@@ -1,23 +1,26 @@
 using System;
 using System.IO;
+
 using Org.BouncyCastle.Crypto.Utilities;
 using Org.BouncyCastle.Utilities;
 
-/**
- * Sparkle v1.2, based on the current round 3 submission, https://sparkle-lwc.github.io/
- * Reference C implementation: https://github.com/cryptolu/sparkle
- * Specification: https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/sparkle-spec-final.pdf
- */
-
 namespace Org.BouncyCastle.Crypto.Digests
 {
-    public class SparkleDigest : IDigest
+    /// <summary>Sparkle v1.2, based on the current round 3 submission, https://sparkle-lwc.github.io/ .</summary>
+    /// <remarks>
+    /// Reference C implementation: https://github.com/cryptolu/sparkle.<br/>
+    /// Specification:
+    /// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/sparkle-spec-final.pdf .
+    /// </remarks>
+    public class SparkleDigest
+        : IDigest
     {
         public enum SparkleParameters
         {
             ESCH256,
             ESCH384
         }
+
         private string algorithmName;
         private readonly uint[] state;
         private MemoryStream message = new MemoryStream();
@@ -36,22 +39,22 @@ namespace Org.BouncyCastle.Crypto.Digests
             int SPARKLE_RATE = 128;
             switch (sparkleParameters)
             {
-                case SparkleParameters.ESCH256:
-                    ESCH_DIGEST_LEN = 256;
-                    SPARKLE_STATE = 384;
-                    SPARKLE_STEPS_SLIM = 7;
-                    SPARKLE_STEPS_BIG = 11;
-                    algorithmName = "ESCH-256";
-                    break;
-                case SparkleParameters.ESCH384:
-                    ESCH_DIGEST_LEN = 384;
-                    SPARKLE_STATE = 512;
-                    SPARKLE_STEPS_SLIM = 8;
-                    SPARKLE_STEPS_BIG = 12;
-                    algorithmName = "ESCH-384";
-                    break;
-                default:
-                    throw new ArgumentException("Invalid definition of SCHWAEMM instance");
+            case SparkleParameters.ESCH256:
+                ESCH_DIGEST_LEN = 256;
+                SPARKLE_STATE = 384;
+                SPARKLE_STEPS_SLIM = 7;
+                SPARKLE_STEPS_BIG = 11;
+                algorithmName = "ESCH-256";
+                break;
+            case SparkleParameters.ESCH384:
+                ESCH_DIGEST_LEN = 384;
+                SPARKLE_STATE = 512;
+                SPARKLE_STEPS_SLIM = 8;
+                SPARKLE_STEPS_BIG = 12;
+                algorithmName = "ESCH-384";
+                break;
+            default:
+                throw new ArgumentException("Invalid definition of SCHWAEMM instance");
             }
             STATE_BRANS = SPARKLE_STATE >> 6;
             STATE_WORDS = SPARKLE_STATE >> 5;
@@ -61,14 +64,9 @@ namespace Org.BouncyCastle.Crypto.Digests
             state = new uint[STATE_WORDS];
         }
 
-        private uint ROT(uint x, int n)
-        {
-            return (((x) >> n) | ((x) << (32 - n)));
-        }
-
         private uint ELL(uint x)
         {
-            return ROT(((x) ^ ((x) << 16)), 16);
+            return Integers.RotateRight(x ^ (x << 16), 16);
         }
 
         private static readonly uint[] RCON = {0xB7E15162, 0xBF715880, 0x38B4DA56, 0x324E7738, 0xBB1185EB, 0x4F7C7B57,
@@ -86,17 +84,17 @@ namespace Org.BouncyCastle.Crypto.Digests
                 for (j = 0; j < 2 * brans; j += 2)
                 {
                     rc = RCON[j >> 1];
-                    state[j] += ROT(state[j + 1], 31);
-                    state[j + 1] ^= ROT(state[j], 24);
+                    state[j] += Integers.RotateRight(state[j + 1], 31);
+                    state[j + 1] ^= Integers.RotateRight(state[j], 24);
                     state[j] ^= rc;
-                    state[j] += ROT(state[j + 1], 17);
-                    state[j + 1] ^= ROT(state[j], 17);
+                    state[j] += Integers.RotateRight(state[j + 1], 17);
+                    state[j + 1] ^= Integers.RotateRight(state[j], 17);
                     state[j] ^= rc;
                     state[j] += state[j + 1];
-                    state[j + 1] ^= ROT(state[j], 31);
+                    state[j + 1] ^= Integers.RotateRight(state[j], 31);
                     state[j] ^= rc;
-                    state[j] += ROT(state[j + 1], 24);
-                    state[j + 1] ^= ROT(state[j], 16);
+                    state[j] += Integers.RotateRight(state[j + 1], 24);
+                    state[j + 1] ^= Integers.RotateRight(state[j], 16);
                     state[j] ^= rc;
                 }
                 // Linear layer
@@ -226,7 +224,6 @@ namespace Org.BouncyCastle.Crypto.Digests
 
         public string AlgorithmName => algorithmName;
 
-
         public void Update(byte input)
         {
             message.Write(new byte[] { input }, 0, 1);
@@ -240,7 +237,7 @@ namespace Org.BouncyCastle.Crypto.Digests
 
         public int GetByteLength()
         {
-            return STATE_WORDS;
+            return RATE_BYTES;
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
@@ -258,7 +255,5 @@ namespace Org.BouncyCastle.Crypto.Digests
             return DIGEST_BYTES;
         }
 #endif
-
     }
 }
-
diff --git a/crypto/src/crypto/engines/AsconEngine.cs b/crypto/src/crypto/engines/AsconEngine.cs
index 21607483d..e8848c3d8 100644
--- a/crypto/src/crypto/engines/AsconEngine.cs
+++ b/crypto/src/crypto/engines/AsconEngine.cs
@@ -224,7 +224,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return DoFinal(outBytes.AsSpan(outOff));
 #else
             if (!initialised)
-                throw new ArgumentException("Need call init function before encryption/decryption");
+                throw new ArgumentException("Need to call Init before encryption/decryption");
 
             if (!aadFinished)
             {
@@ -265,7 +265,7 @@ namespace Org.BouncyCastle.Crypto.Engines
                 Reset(true);
 
                 if (result != 0UL)
-                    throw new ArgumentException("Mac does not match");
+                    throw new InvalidCipherTextException("mac check in " + AlgorithmName + " failed");
 
                 return len;
             }
@@ -276,7 +276,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         public int DoFinal(Span<byte> output)
         {
             if (!initialised)
-                throw new ArgumentException("Need call init function before encryption/decryption");
+                throw new ArgumentException("Need to call Init before encryption/decryption");
 
             if (!aadFinished)
             {
@@ -317,7 +317,7 @@ namespace Org.BouncyCastle.Crypto.Engines
                 Reset(true);
 
                 if (result != 0UL)
-                    throw new ArgumentException("Mac does not match");
+                    throw new InvalidCipherTextException("mac check in " + AlgorithmName + " failed");
 
                 return len;
             }
@@ -838,7 +838,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         private void Reset(bool clearMac)
         {
             if (!initialised)
-                throw new ArgumentException("Need call init function before encryption/decryption");
+                throw new ArgumentException("Need to call Init function before encryption/decryption");
 
             x0 = x1 = x2 = x3 = x4 = 0;
             ascon_aeadinit();
diff --git a/crypto/src/crypto/engines/ElephantEngine.cs b/crypto/src/crypto/engines/ElephantEngine.cs
index d5f05f658..4e0e3216d 100644
--- a/crypto/src/crypto/engines/ElephantEngine.cs
+++ b/crypto/src/crypto/engines/ElephantEngine.cs
@@ -70,32 +70,32 @@ namespace Org.BouncyCastle.Crypto.Engines
         {
             switch (parameters)
             {
-                case ElephantParameters.elephant160:
-                    BLOCK_SIZE = 20;
-                    nBits = 160;
-                    nSBox = 20;
-                    nRounds = 80;
-                    lfsrIV = 0x75;
-                    CRYPTO_ABYTES = 8;
-                    algorithmName = "Elephant 160 AEAD";
-                    break;
-                case ElephantParameters.elephant176:
-                    BLOCK_SIZE = 22;
-                    nBits = 176;
-                    nSBox = 22;
-                    nRounds = 90;
-                    lfsrIV = 0x45;
-                    CRYPTO_ABYTES = 8;
-                    algorithmName = "Elephant 176 AEAD";
-                    break;
-                case ElephantParameters.elephant200:
-                    BLOCK_SIZE = 25;
-                    nRounds = 18;
-                    CRYPTO_ABYTES = 16;
-                    algorithmName = "Elephant 200 AEAD";
-                    break;
-                default:
-                    throw new ArgumentException("Invalid parameter settings for Elephant");
+            case ElephantParameters.elephant160:
+                BLOCK_SIZE = 20;
+                nBits = 160;
+                nSBox = 20;
+                nRounds = 80;
+                lfsrIV = 0x75;
+                CRYPTO_ABYTES = 8;
+                algorithmName = "Elephant 160 AEAD";
+                break;
+            case ElephantParameters.elephant176:
+                BLOCK_SIZE = 22;
+                nBits = 176;
+                nSBox = 22;
+                nRounds = 90;
+                lfsrIV = 0x45;
+                CRYPTO_ABYTES = 8;
+                algorithmName = "Elephant 176 AEAD";
+                break;
+            case ElephantParameters.elephant200:
+                BLOCK_SIZE = 25;
+                nRounds = 18;
+                CRYPTO_ABYTES = 16;
+                algorithmName = "Elephant 200 AEAD";
+                break;
+            default:
+                throw new ArgumentException("Invalid parameter settings for Elephant");
             }
             this.parameters = parameters;
             initialised = false;
@@ -316,40 +316,25 @@ namespace Org.BouncyCastle.Crypto.Engines
             }
         }
 
-
-
         public void Init(bool forEncryption, ICipherParameters param)
         {
             this.forEncryption = forEncryption;
-            if (!(param is ParametersWithIV))
-            {
-                throw new ArgumentException(
-                    "Elephant init parameters must include an IV");
-            }
 
-            ParametersWithIV ivParams = (ParametersWithIV)param;
+            if (!(param is ParametersWithIV ivParams))
+                throw new ArgumentException("Elephant init parameters must include an IV");
 
             npub = ivParams.GetIV();
 
             if (npub == null || npub.Length != 12)
-            {
-                throw new ArgumentException(
-                    "Elephant requires exactly 12 bytes of IV");
-            }
+                throw new ArgumentException("Elephant requires exactly 12 bytes of IV");
 
-            if (!(ivParams.Parameters is KeyParameter))
-            {
-                throw new ArgumentException(
-                    "Elephant init parameters must include a key");
-            }
+            if (!(ivParams.Parameters is KeyParameter key))
+                throw new ArgumentException("Elephant init parameters must include a key");
 
-            KeyParameter key = (KeyParameter)ivParams.Parameters;
             byte[] k = key.GetKey();
             if (k.Length != 16)
-            {
-                throw new ArgumentException(
-                    "Elephant key must be 128 bits long");
-            }
+                throw new ArgumentException("Elephant key must be 128 bits long");
+
             // Storage for the expanded key L
             expanded_key = new byte[BLOCK_SIZE];
             Array.Copy(k, 0, expanded_key, 0, CRYPTO_KEYBYTES);
@@ -365,44 +350,35 @@ namespace Org.BouncyCastle.Crypto.Engines
 
         public void ProcessAadByte(byte input)
         {
-            aadData.Write(new byte[] { input }, 0, 1);
+            aadData.WriteByte(input);
         }
 
-
-        public void ProcessAadBytes(byte[] input, int inOff, int len)
+        public void ProcessAadBytes(byte[] inBytes, int inOff, int len)
         {
-            if (inOff + len > input.Length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
-            aadData.Write(input, inOff, len);
-        }
+            Check.DataLength(inBytes, inOff, len, "input buffer too short");
 
+            aadData.Write(inBytes, inOff, len);
+        }
 
-        public int ProcessByte(byte input, byte[] output, int outOff)
+        public int ProcessByte(byte input, byte[] outBytes, int outOff)
         {
-            message.Write(new byte[] { input }, 0, 1);
+            message.Write(new byte[]{ input }, 0, 1);
             return 0;
         }
 
-
-        public int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
+        public int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff)
         {
-            if (inOff + len > input.Length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
-            message.Write(input, inOff, len);
+            Check.DataLength(inBytes, inOff, len, "input buffer too short");
+
+            message.Write(inBytes, inOff, len);
             return 0;
         }
 
-
         public int DoFinal(byte[] output, int outOff)
         {
             if (!initialised)
-            {
-                throw new ArgumentException("Need call init function before encryption/decryption");
-            }
+                throw new ArgumentException("Need to call Init function before encryption/decryption");
+
             int mlen = (int)message.Length - (forEncryption ? 0 : CRYPTO_ABYTES);
             if ((forEncryption && mlen + outOff + CRYPTO_ABYTES > output.Length) ||
             (!forEncryption && mlen + outOff - CRYPTO_ABYTES > output.Length))
@@ -493,37 +469,28 @@ namespace Org.BouncyCastle.Crypto.Engines
             }
             else
             {
-                for (int i = 0; i < CRYPTO_ABYTES; ++i)
-                {
-                    if (tag_buffer[i] != m[mlen + i])
-                    {
-                        throw new ArgumentException("Mac does not match");
-                    }
-                }
+                if (!Arrays.FixedTimeEquals(CRYPTO_ABYTES, tag_buffer, 0, m, mlen))
+                    throw new InvalidCipherTextException("mac check in " + AlgorithmName + " failed");
             }
             reset(false);
             return mlen;
         }
 
-
         public byte[] GetMac()
         {
             return tag;
         }
 
-
         public int GetUpdateOutputSize(int len)
         {
             return len;
         }
 
-
         public int GetOutputSize(int len)
         {
             return len + CRYPTO_ABYTES;
         }
 
-
         public void Reset()
         {
             reset(true);
@@ -547,7 +514,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 
         public int ProcessByte(byte input, Span<byte> output)
         {
-            message.Write(new byte[] { input });
+            message.Write(new byte[]{ input });
             return 0;
         }
 
diff --git a/crypto/src/crypto/engines/ISAPEngine.cs b/crypto/src/crypto/engines/ISAPEngine.cs
index 8be5a79b2..7ef629d1b 100644
--- a/crypto/src/crypto/engines/ISAPEngine.cs
+++ b/crypto/src/crypto/engines/ISAPEngine.cs
@@ -921,7 +921,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return DoFinal(outBytes.AsSpan(outOff));
 #else
             if (!initialised)
-                throw new ArgumentException("Need call init function before encryption/decryption");
+                throw new ArgumentException("Need to call Init before encryption/decryption");
 
             byte[] aad = aadData.GetBuffer();
             byte[] msg = message.GetBuffer();
@@ -949,7 +949,8 @@ namespace Org.BouncyCastle.Crypto.Engines
                 ISAPAEAD.isap_mac(aad, aadLen, msg, outLen, mac, 0);
                 ISAPAEAD.reset();
                 if (!Arrays.FixedTimeEquals(16, mac, 0, msg, outLen))
-                    throw new ArgumentException("Mac does not match");
+                    throw new InvalidCipherTextException("mac check in " + AlgorithmName + " failed");
+
                 ISAPAEAD.isap_enc(msg, 0, outLen, outBytes, outOff);
             }
             return outLen;
@@ -960,7 +961,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         public int DoFinal(Span<byte> output)
         {
             if (!initialised)
-                throw new ArgumentException("Need call init function before encryption/decryption");
+                throw new ArgumentException("Need to call Init before encryption/decryption");
 
             byte[] aad = aadData.GetBuffer();
             byte[] msg = message.GetBuffer();
@@ -988,7 +989,8 @@ namespace Org.BouncyCastle.Crypto.Engines
                 ISAPAEAD.isap_mac(aad, aadLen, msg, outLen, mac, 0);
                 ISAPAEAD.reset();
                 if (!Arrays.FixedTimeEquals(16, mac, 0, msg, outLen))
-                    throw new ArgumentException("Mac does not match");
+                    throw new InvalidCipherTextException("mac check in " + AlgorithmName + " failed");
+
                 ISAPAEAD.isap_enc(msg.AsSpan(0, outLen), output);
             }
             return outLen;
@@ -1022,7 +1024,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         public void Reset()
         {
             if (!initialised)
-                throw new ArgumentException("Need call init function before encryption/decryption");
+                throw new ArgumentException("Need to call Init before encryption/decryption");
 
             aadData.SetLength(0);
             ISAPAEAD.reset();
diff --git a/crypto/src/crypto/engines/PhotonBeetleEngine.cs b/crypto/src/crypto/engines/PhotonBeetleEngine.cs
index 0d5728a76..e593bb6f5 100644
--- a/crypto/src/crypto/engines/PhotonBeetleEngine.cs
+++ b/crypto/src/crypto/engines/PhotonBeetleEngine.cs
@@ -3,6 +3,7 @@ using System.IO;
 
 using Org.BouncyCastle.Crypto.Modes;
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Engines
 {
@@ -150,37 +151,32 @@ namespace Org.BouncyCastle.Crypto.Engines
             aadData.Write(new byte[] { input }, 0, 1);
         }
 
-        public void ProcessAadBytes(byte[] input, int inOff, int len)
+        public void ProcessAadBytes(byte[] inBytes, int inOff, int len)
         {
-            if (inOff + len > input.Length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
-            aadData.Write(input, inOff, len);
+            Check.DataLength(inBytes, inOff, len, "input buffer too short");
+
+            aadData.Write(inBytes, inOff, len);
         }
 
-        public int ProcessByte(byte input, byte[] output, int outOff)
+        public int ProcessByte(byte input, byte[] outBytes, int outOff)
         {
-            message.Write(new byte[] { input }, 0, 1);
+            message.WriteByte(input);
             return 0;
         }
 
-        public int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
+        public int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff)
         {
-            if (inOff + len > input.Length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
-            message.Write(input, inOff, len);
+            Check.DataLength(inBytes, inOff, len, "input buffer too short");
+
+            message.Write(inBytes, inOff, len);
             return 0;
         }
 
         public void Reset()
         {
             if (!initialised)
-            {
-                throw new ArgumentException("Need call init function before encryption/decryption");
-            }
+                throw new ArgumentException("Need to call Init before encryption/decryption");
+
             reset(true);
         }
 
@@ -323,9 +319,8 @@ namespace Org.BouncyCastle.Crypto.Engines
         public int DoFinal(byte[] output, int outOff)
         {
             if (!initialised)
-            {
-                throw new ArgumentException("Need call init function before encryption/decryption");
-            }
+                throw new ArgumentException("Need to call Init before encryption/decryption");
+
             int len = (int)message.Length - (forEncryption ? 0 : TAG_INBYTES);
             if ((forEncryption && len + TAG_INBYTES + outOff > output.Length) ||
                 (!forEncryption && len + outOff > output.Length))
@@ -392,13 +387,8 @@ namespace Org.BouncyCastle.Crypto.Engines
             }
             else
             {
-                for (i = 0; i < TAG_INBYTES; ++i)
-                {
-                    if (T[i] != input[len + i])
-                    {
-                        throw new ArgumentException("Mac does not match");
-                    }
-                }
+                if (!Arrays.FixedTimeEquals(TAG_INBYTES, T, 0, input, len))
+                    throw new InvalidCipherTextException("mac check in " + AlgorithmName + " failed");
             }
             reset(false);
             return len;
diff --git a/crypto/src/crypto/engines/SparkleEngine.cs b/crypto/src/crypto/engines/SparkleEngine.cs
index bbdce7ef8..63e400d4c 100644
--- a/crypto/src/crypto/engines/SparkleEngine.cs
+++ b/crypto/src/crypto/engines/SparkleEngine.cs
@@ -1,19 +1,21 @@
 using System;
 using System.IO;
+
 using Org.BouncyCastle.Crypto.Modes;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Crypto.Utilities;
 using Org.BouncyCastle.Utilities;
 
-/**
- * Sparkle v1.2, based on the current round 3 submission, https://sparkle-lwc.github.io/
- * Reference C implementation: https://github.com/cryptolu/sparkle
- * Specification: https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/sparkle-spec-final.pdf
- */
-
 namespace Org.BouncyCastle.Crypto.Engines
 {
-    public class SparkleEngine : IAeadBlockCipher
+    /// <summary>Sparkle v1.2, based on the current round 3 submission, https://sparkle-lwc.github.io/ .</summary>
+    /// <remarks>
+    /// Reference C implementation: https://github.com/cryptolu/sparkle.<br/>
+    /// Specification:
+    /// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/sparkle-spec-final.pdf .
+    /// </remarks>
+    public class SparkleEngine
+        : IAeadBlockCipher
     {
         public enum SparkleParameters
         {
@@ -481,13 +483,8 @@ namespace Org.BouncyCastle.Crypto.Engines
             }
             else
             {
-                for (i = 0; i < TAG_BYTES; ++i)
-                {
-                    if (tag[i] != input[inlen + i])
-                    {
-                        throw new ArgumentException(algorithmName + " mac does not match");
-                    }
-                }
+                if (!Arrays.FixedTimeEquals(TAG_BYTES, tag, 0, input, inlen))
+                    throw new InvalidCipherTextException("mac check in " + AlgorithmName + " failed");
             }
             reset(false);
             return inlen;
diff --git a/crypto/src/crypto/engines/XoodyakEngine.cs b/crypto/src/crypto/engines/XoodyakEngine.cs
index caf49e53c..e0263272e 100644
--- a/crypto/src/crypto/engines/XoodyakEngine.cs
+++ b/crypto/src/crypto/engines/XoodyakEngine.cs
@@ -126,27 +126,20 @@ namespace Org.BouncyCastle.Crypto.Engines
         public int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
         {
             if (!initialised)
-            {
-                throw new ArgumentException("Need call init function before encryption/decryption");
-            }
+                throw new ArgumentException("Need to call Init before encryption/decryption");
+
             if (mode != MODE.ModeKeyed)
-            {
                 throw new ArgumentException("Xoodyak has not been initialised");
-            }
-            if (inOff + len > input.Length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
+
+            Check.DataLength(input, inOff, len, "input buffer too short");
+
             message.Write(input, inOff, len);
             int blockLen = (int)message.Length - (forEncryption ? 0 : TAGLEN);
             if (blockLen >= GetBlockSize())
             {
                 byte[] blocks = message.GetBuffer();
                 len = blockLen / GetBlockSize() * GetBlockSize();
-                if (len + outOff > output.Length)
-                {
-                    throw new OutputLengthException("output buffer is too short");
-                }
+                Check.OutputLength(output, outOff, len, "output buffer is too short");
                 processAAD();
                 encrypt(blocks, 0, len, output, outOff);
                 int messageLen = (int)message.Length;
@@ -196,9 +189,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         public int DoFinal(byte[] output, int outOff)
         {
             if (!initialised)
-            {
-                throw new ArgumentException("Need call init function before encryption/decryption");
-            }
+                throw new ArgumentException("Need to call Init before encryption/decryption");
 
             byte[] blocks = message.GetBuffer();
             int len = (int)message.Length;
@@ -225,43 +216,34 @@ namespace Org.BouncyCastle.Crypto.Engines
                 encrypt(blocks, 0, inOff, output, outOff);
                 tag = new byte[TAGLEN];
                 Up(tag, TAGLEN, 0x40);
-                for (int i = 0; i < TAGLEN; ++i)
-                {
-                    if (tag[i] != blocks[inOff++])
-                    {
-                        throw new ArgumentException("Mac does not match");
-                    }
-                }
+
+                if (!Arrays.FixedTimeEquals(TAGLEN, tag, 0, blocks, inOff))
+                    throw new InvalidCipherTextException("mac check in " + AlgorithmName + " failed");
             }
             reset(false);
             return rv;
         }
 
-
         public byte[] GetMac()
         {
             return tag;
         }
 
-
         public int GetUpdateOutputSize(int len)
         {
             return len;
         }
 
-
         public int GetOutputSize(int len)
         {
             return len + TAGLEN;
         }
 
-
         public void Reset()
         {
             if (!initialised)
-            {
-                throw new ArgumentException("Need call init function before encryption/decryption");
-            }
+                throw new ArgumentException("Need to call Init before encryption/decryption");
+
             reset(true);
         }