summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crypto/src/crypto/engines/AsconEngine.cs976
-rw-r--r--crypto/test/src/crypto/test/AsconTest.cs337
2 files changed, 721 insertions, 592 deletions
diff --git a/crypto/src/crypto/engines/AsconEngine.cs b/crypto/src/crypto/engines/AsconEngine.cs
index 281c4b0df..6f70ee43b 100644
--- a/crypto/src/crypto/engines/AsconEngine.cs
+++ b/crypto/src/crypto/engines/AsconEngine.cs
@@ -1,8 +1,13 @@
 using System;
 using System.IO;
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+using System.Runtime.CompilerServices;
+#endif
 
 using Org.BouncyCastle.Crypto.Modes;
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Engines
 {
@@ -13,8 +18,8 @@ namespace Org.BouncyCastle.Crypto.Engines
     * ASCON AEAD v1.2 with reference to C Reference Impl from: https://github.com/ascon/ascon-c
     * </p>
     */
-    public class AsconEngine
-        : IAeadBlockCipher
+    public sealed class AsconEngine
+        : IAeadCipher
     {
         public enum AsconParameters
         {
@@ -46,9 +51,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         private ulong x2;
         private ulong x3;
         private ulong x4;
-        private String algorithmName;
-
-        public IBlockCipher UnderlyingCipher => throw new NotImplementedException();
+        private string algorithmName;
 
         public string AlgorithmName => algorithmName;
 
@@ -57,136 +60,301 @@ namespace Org.BouncyCastle.Crypto.Engines
             this.asconParameters = asconParameters;
             switch (asconParameters)
             {
-                case AsconParameters.ascon80pq:
-                    CRYPTO_KEYBYTES = 20;
-                    CRYPTO_ABYTES = 16;
-                    ASCON_AEAD_RATE = 8;
-                    ASCON_IV = 0xa0400c0600000000UL;
-                    algorithmName = "Ascon-80pq AEAD";
-                    break;
-                case AsconParameters.ascon128a:
-                    CRYPTO_KEYBYTES = 16;
-                    CRYPTO_ABYTES = 16;
-                    ASCON_AEAD_RATE = 16;
-                    ASCON_IV = 0x80800c0800000000UL;
-                    algorithmName = "Ascon-128a AEAD";
-                    break;
-                case AsconParameters.ascon128:
-                    CRYPTO_KEYBYTES = 16;
-                    CRYPTO_ABYTES = 16;
-                    ASCON_AEAD_RATE = 8;
-                    ASCON_IV = 0x80400c0600000000UL;
-                    algorithmName = "Ascon-128 AEAD";
-                    break;
-                default:
-                    throw new ArgumentException("invalid parameter setting for ASCON AEAD");
+            case AsconParameters.ascon80pq:
+                CRYPTO_KEYBYTES = 20;
+                CRYPTO_ABYTES = 16;
+                ASCON_AEAD_RATE = 8;
+                ASCON_IV = 0xa0400c0600000000UL;
+                algorithmName = "Ascon-80pq AEAD";
+                break;
+            case AsconParameters.ascon128a:
+                CRYPTO_KEYBYTES = 16;
+                CRYPTO_ABYTES = 16;
+                ASCON_AEAD_RATE = 16;
+                ASCON_IV = 0x80800c0800000000UL;
+                algorithmName = "Ascon-128a AEAD";
+                break;
+            case AsconParameters.ascon128:
+                CRYPTO_KEYBYTES = 16;
+                CRYPTO_ABYTES = 16;
+                ASCON_AEAD_RATE = 8;
+                ASCON_IV = 0x80400c0600000000UL;
+                algorithmName = "Ascon-128 AEAD";
+                break;
+            default:
+                throw new ArgumentException("invalid parameter setting for ASCON AEAD");
             }
             nr = (ASCON_AEAD_RATE == 8) ? 6 : 8;
             initialised = false;
         }
 
-        private ulong U64BIG(ulong x)
+        public int GetKeyBytesSize()
         {
-            return (((0x00000000000000FFUL & x) << 56) |
-            ((0x000000000000FF00UL & x) << 40) |
-            ((0x0000000000FF0000UL & x) << 24) |
-            ((0x00000000FF000000UL & x) << 8) |
-            ((0x000000FF00000000UL & x) >> 8) |
-            ((0x0000FF0000000000UL & x) >> 24) |
-            ((0x00FF000000000000UL & x) >> 40) |
-            ((0xFF00000000000000UL & x) >> 56));
+            return CRYPTO_KEYBYTES;
         }
 
-        private ulong ROR(ulong x, int n)
+        public int GetIVBytesSize()
         {
-            return x >> n | x << (64 - n);
+            return CRYPTO_ABYTES;
         }
 
-        private ulong KEYROT(ulong lo2hi, ulong hi2lo)
+        public void Init(bool forEncryption, ICipherParameters parameters)
         {
-            return lo2hi << 32 | hi2lo >> 32;
+            this.forEncryption = forEncryption;
+            if (!(parameters is ParametersWithIV withIV))
+                throw new ArgumentException("ASCON Init parameters must include an IV");
+
+            byte[] npub = withIV.GetIV();
+            if (npub == null || npub.Length != CRYPTO_ABYTES)
+                throw new ArgumentException(asconParameters + " requires exactly " + CRYPTO_ABYTES + " bytes of IV");
+
+            if (!(withIV.Parameters is KeyParameter key))
+                throw new ArgumentException("ASCON Init parameters must include a key");
+
+            byte[] k = key.GetKey();
+            if (k.Length != CRYPTO_KEYBYTES)
+                throw new ArgumentException(asconParameters + " key must be " + CRYPTO_KEYBYTES + " bytes long");
+
+            N0 = Pack.BE_To_UInt64(npub, 0);
+            N1 = Pack.BE_To_UInt64(npub, 8);
+            if (CRYPTO_KEYBYTES == 16)
+            {
+                K1 = Pack.BE_To_UInt64(k, 0);
+                K2 = Pack.BE_To_UInt64(k, 8);
+            }
+            else if (CRYPTO_KEYBYTES == 20)
+            {
+                K0 = Pack.BE_To_UInt32(k, 0);
+                K1 = Pack.BE_To_UInt64(k, 4);
+                K2 = Pack.BE_To_UInt64(k, 12);
+            }
+            initialised = true;
+            /*Mask-Gen*/
+            Reset(false);
         }
 
-        private ulong PAD(int i)
+        public void ProcessAadByte(byte input)
         {
-            return 0x80UL << (56 - (i << 3));
+            if (aadFinished)
+            {
+                throw new ArgumentException("AAD cannot be added after reading a full block(" + ASCON_AEAD_RATE +
+                    " bytes) of input for " + (forEncryption ? "encryption" : "decryption"));
+            }
+
+            aadData.WriteByte(input);
         }
 
-        private ulong MASK(int n)
+        public void ProcessAadBytes(byte[] inBytes, int inOff, int len)
         {
-            /* undefined for n == 0 */
-            return ~0UL >> (64 - (n << 3));
+            if (aadFinished)
+            {
+                throw new ArgumentException("AAD cannot be added after reading a full block(" + ASCON_AEAD_RATE +
+                    " bytes) of input for " + (forEncryption ? "encryption" : "decryption"));
+            }
+
+            Check.DataLength(inBytes, inOff, len, "input buffer too short");
+
+            aadData.Write(inBytes, inOff, len);
         }
 
-        private ulong LOAD(byte[] bytes, int inOff, int n)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void ProcessAadBytes(ReadOnlySpan<byte> input)
         {
-            ulong x = 0;
-            int len = System.Math.Min(8, bytes.Length - inOff);
-            for (int i = 0; i < len; ++i)
+            if (aadFinished)
             {
-                x |= (bytes[i + inOff] & 0xFFUL) << (i << 3);
+                throw new ArgumentException("AAD cannot be added after reading a full block(" + ASCON_AEAD_RATE +
+                    " bytes) of input for " + (forEncryption ? "encryption" : "decryption"));
             }
-            return U64BIG(x & MASK(n));
+
+            aadData.Write(input);
+        }
+#endif
+
+        public int ProcessByte(byte input, byte[] outBytes, int outOff)
+        {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return ProcessByte(input, Spans.FromNullable(outBytes, outOff));
+#else
+            return ProcessBytes(new byte[]{ input }, 0, 1, outBytes, outOff);
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int ProcessByte(byte input, Span<byte> output)
+        {
+            Span<byte> singleByte = stackalloc byte[1]{ input };
+
+            return ProcessBytes(singleByte, output);
         }
+#endif
 
-        private void STORE(byte[] bytes, int inOff, ulong w, int n)
+        public int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff)
         {
-            ulong x = 0;
-            for (int i = 0; i < n; ++i)
+            Check.DataLength(inBytes, inOff, len, "input buffer too short");
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return ProcessBytes(inBytes.AsSpan(inOff, len), Spans.FromNullable(outBytes, outOff));
+#else
+            if (!initialised)
+                throw new ArgumentException("Need to call Init function before encryption/decryption");
+
+            message.Write(inBytes, inOff, len);
+            int rv = ProcessBytes(outBytes, outOff);
+            encrypted = true;
+            return rv;
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
+        {
+            if (!initialised)
+                throw new ArgumentException("Need to call Init function before encryption/decryption");
+
+            message.Write(input);
+            int rv = ProcessBytes(output);
+            encrypted = true;
+            return rv;
+        }
+#endif
+
+        public int DoFinal(byte[] outBytes, int outOff)
+        {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return DoFinal(outBytes.AsSpan(outOff));
+#else
+            if (!initialised)
+                throw new ArgumentException("Need call init function before encryption/decryption");
+
+            if (!aadFinished)
+            {
+                ProcessAad();
+            }
+            if (!encrypted)
+            {
+                ProcessBytes(Array.Empty<byte>(), 0, 0, Array.Empty<byte>(), 0);
+            }
+            byte[] input = message.GetBuffer();
+            int len = Convert.ToInt32(message.Length);
+            if (forEncryption)
             {
-                x |= (bytes[i + inOff] & 0xFFUL) << (i << 3);
+                Check.OutputLength(outBytes, outOff, len + CRYPTO_ABYTES, "output buffer too short");
             }
-            x &= ~MASK(n);
-            x |= U64BIG(w);
-            for (int i = 0; i < n; ++i)
+            else
             {
-                bytes[i + inOff] = (byte)(x >> (i << 3));
+                Check.OutputLength(outBytes, outOff, len - CRYPTO_ABYTES, "output buffer too short");
             }
+            if (forEncryption)
+            {
+                ascon_final(outBytes, outOff, input, 0, len);
+                /* set tag */
+                mac = new byte[16];
+                Pack.UInt64_To_BE(x3, mac, 0);
+                Pack.UInt64_To_BE(x4, mac, 8);
+                Array.Copy(mac, 0, outBytes, len + outOff, 16);
+                Reset(false);
+                return len + CRYPTO_ABYTES;
+            }
+            else
+            {
+                len -= CRYPTO_ABYTES;
+                ascon_final(outBytes, outOff, input, 0, len);
+                x3 ^= Pack.BE_To_UInt64(input, len);
+                x4 ^= Pack.BE_To_UInt64(input, len + 8);
+                ulong result = x3 | x4;
+                Reset(true);
+
+                if (result != 0UL)
+                    throw new ArgumentException("Mac does not match");
+
+                return len;
+            }
+#endif
         }
 
-        private ulong LOADBYTES(byte[] bytes, int inOff, int n)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int DoFinal(Span<byte> output)
         {
-            ulong x = 0;
-            for (int i = 0; i < n; ++i)
+            if (!initialised)
+                throw new ArgumentException("Need call init function before encryption/decryption");
+
+            if (!aadFinished)
+            {
+                ProcessAad();
+            }
+            if (!encrypted)
+            {
+                ProcessBytes(Array.Empty<byte>(), 0, 0, Array.Empty<byte>(), 0);
+            }
+            byte[] input = message.GetBuffer();
+            int len = Convert.ToInt32(message.Length);
+            if (forEncryption)
+            {
+                Check.OutputLength(output, len + CRYPTO_ABYTES, "output buffer too short");
+            }
+            else
+            {
+                Check.OutputLength(output, len - CRYPTO_ABYTES, "output buffer too short");
+            }
+            if (forEncryption)
+            {
+                ascon_final(output, input.AsSpan(0, len));
+                /* set tag */
+                mac = new byte[CRYPTO_ABYTES];
+                Pack.UInt64_To_BE(x3, mac, 0);
+                Pack.UInt64_To_BE(x4, mac, 8);
+                mac.AsSpan(0, CRYPTO_ABYTES).CopyTo(output[len..]);
+                Reset(false);
+                return len + CRYPTO_ABYTES;
+            }
+            else
             {
-                x |= (bytes[i + inOff] & 0xFFUL) << ((7 - i) << 3);
+                len -= CRYPTO_ABYTES;
+                ascon_final(output, input.AsSpan(0, len));
+                x3 ^= Pack.BE_To_UInt64(input, len);
+                x4 ^= Pack.BE_To_UInt64(input, len + 8);
+                ulong result = x3 | x4;
+                Reset(true);
+
+                if (result != 0UL)
+                    throw new ArgumentException("Mac does not match");
+
+                return len;
             }
-            return x;
         }
+#endif
 
-        private void STOREBYTES(byte[] bytes, int inOff, ulong w, int n)
+        public byte[] GetMac()
         {
-            for (int i = 0; i < n; ++i)
-            {
-                bytes[i + inOff] = (byte)(w >> ((7 - i) << 3));
-            }
+            return mac;
+        }
+
+        public int GetUpdateOutputSize(int len)
+        {
+            return len;
         }
 
-        private void ROUND(ulong C)
+        public int GetOutputSize(int len)
         {
-            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);
+            return len + CRYPTO_ABYTES;
+        }
+
+        public void Reset()
+        {
+            Reset(true);
         }
 
         private void P(int nr)
         {
-            if (nr == 12)
-            {
-                ROUND(0xf0UL);
-                ROUND(0xe1UL);
-                ROUND(0xd2UL);
-                ROUND(0xc3UL);
-            }
             if (nr >= 8)
             {
+                if (nr == 12)
+                {
+                    ROUND(0xf0UL);
+                    ROUND(0xe1UL);
+                    ROUND(0xd2UL);
+                    ROUND(0xc3UL);
+                }
                 ROUND(0xb4UL);
                 ROUND(0xa5UL);
             }
@@ -198,6 +366,39 @@ namespace Org.BouncyCastle.Crypto.Engines
             ROUND(0x4bUL);
         }
 
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+        private void ROUND(ulong c)
+        {
+            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);
+        }
+
+        private void ProcessAad()
+        {
+            if (!aadFinished)
+            {
+                byte[] ad = aadData.GetBuffer();
+                int adlen = Convert.ToInt32(aadData.Length);
+                /* perform ascon computation */
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                ascon_adata(ad.AsSpan(0, adlen));
+#else
+                ascon_adata(ad, 0, adlen);
+#endif
+                aadFinished = true;
+            }
+        }
+
         private void ascon_aeadinit()
         {
             /* initialize */
@@ -219,40 +420,72 @@ namespace Org.BouncyCastle.Crypto.Engines
             x4 ^= K2;
         }
 
-        private void ascon_adata(byte[] ad, int adOff, int adlen)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private int ProcessBytes(Span<byte> output)
         {
-            if (adlen != 0)
+            int len_orig = Convert.ToInt32(message.Length);
+            int len = 0;
+            if (forEncryption)
+            {
+                if (len_orig >= ASCON_AEAD_RATE)
+                {
+                    ProcessAad();
+                    byte[] input = message.GetBuffer();
+                    len = (len_orig / ASCON_AEAD_RATE) * ASCON_AEAD_RATE;
+                    Check.OutputLength(output, len, "output buffer is too short");
+                    ascon_encrypt(output, input.AsSpan(0, len));
+                    message.SetLength(0);
+                    message.Write(input, len, len_orig - len);
+                }
+            }
+            else
+            {
+                if (len_orig - CRYPTO_ABYTES >= ASCON_AEAD_RATE)
+                {
+                    ProcessAad();
+                    byte[] input = message.GetBuffer();
+                    len = ((len_orig - CRYPTO_ABYTES) / ASCON_AEAD_RATE) * ASCON_AEAD_RATE;
+                    Check.OutputLength(output, len, "output buffer is too short");
+                    ascon_decrypt(output, input.AsSpan(0, len));
+                    message.SetLength(0);
+                    message.Write(input, len, len_orig - len);
+                }
+            }
+            return len;
+        }
+
+        private void ascon_adata(ReadOnlySpan<byte> aad)
+        {
+            if (!aad.IsEmpty)
             {
                 /* full associated data blocks */
-                while (adlen >= ASCON_AEAD_RATE)
+                while (aad.Length >= ASCON_AEAD_RATE)
                 {
-                    x0 ^= LOAD(ad, adOff, 8);
+                    x0 ^= Pack.BE_To_UInt64(aad);
                     if (ASCON_AEAD_RATE == 16)
                     {
-                        x1 ^= LOAD(ad, adOff + 8, 8);
+                        x1 ^= Pack.BE_To_UInt64(aad[8..]);
                     }
                     P(nr);
-                    adOff += ASCON_AEAD_RATE;
-                    adlen -= ASCON_AEAD_RATE;
+                    aad = aad[ASCON_AEAD_RATE..];
                 }
-                /* readonly associated data block */
-                if (ASCON_AEAD_RATE == 16 && adlen >= 8)
+                /* final associated data block */
+                if (ASCON_AEAD_RATE == 16 && aad.Length >= 8)
                 {
-                    x0 ^= LOAD(ad, adOff, 8);
-                    adOff += 8;
-                    adlen -= 8;
-                    x1 ^= PAD(adlen);
-                    if (adlen != 0)
+                    x0 ^= Pack.BE_To_UInt64(aad);
+                    aad = aad[8..];
+                    x1 ^= PAD(aad.Length);
+                    if (!aad.IsEmpty)
                     {
-                        x1 ^= LOAD(ad, adOff, adlen);
+                        x1 ^= Pack.BE_To_UInt64_High(aad);
                     }
                 }
                 else
                 {
-                    x0 ^= PAD(adlen);
-                    if (adlen != 0)
+                    x0 ^= PAD(aad.Length);
+                    if (!aad.IsEmpty)
                     {
-                        x0 ^= LOAD(ad, adOff, adlen);
+                        x0 ^= Pack.BE_To_UInt64_High(aad);
                     }
                 }
                 P(nr);
@@ -261,115 +494,104 @@ namespace Org.BouncyCastle.Crypto.Engines
             x4 ^= 1UL;
         }
 
-        private void ascon_encrypt(byte[] c, int cOff, byte[] m, int mOff, int mlen)
+        private void ascon_encrypt(Span<byte> c, ReadOnlySpan<byte> m)
         {
             /* full plaintext blocks */
-            while (mlen >= ASCON_AEAD_RATE)
+            while (m.Length >= ASCON_AEAD_RATE)
             {
-                x0 ^= LOAD(m, mOff, 8);
-                STORE(c, cOff, x0, 8);
+                x0 ^= Pack.BE_To_UInt64(m);
+                Pack.UInt64_To_BE(x0, c);
                 if (ASCON_AEAD_RATE == 16)
                 {
-                    x1 ^= LOAD(m, mOff + 8, 8);
-                    STORE(c, cOff + 8, x1, 8);
+                    x1 ^= Pack.BE_To_UInt64(m[8..]);
+                    Pack.UInt64_To_BE(x1, c[8..]);
                 }
                 P(nr);
-                mOff += ASCON_AEAD_RATE;
-                cOff += ASCON_AEAD_RATE;
-                mlen -= ASCON_AEAD_RATE;
+                m = m[ASCON_AEAD_RATE..];
+                c = c[ASCON_AEAD_RATE..];
             }
         }
 
-        private void ascon_decrypt(byte[] m, int mOff, byte[] c, int cOff, int clen)
+        private void ascon_decrypt(Span<byte> m, ReadOnlySpan<byte> c)
         {
             /* full ciphertext blocks */
-            while (clen >= ASCON_AEAD_RATE)
+            while (c.Length >= ASCON_AEAD_RATE)
             {
-                ulong cx = LOAD(c, cOff, 8);
+                ulong cx = Pack.BE_To_UInt64(c);
                 x0 ^= cx;
-                STORE(m, mOff, x0, 8);
+                Pack.UInt64_To_BE(x0, m);
                 x0 = cx;
                 if (ASCON_AEAD_RATE == 16)
                 {
-                    cx = LOAD(c, cOff + 8, 8);
+                    cx = Pack.BE_To_UInt64(c[8..]);
                     x1 ^= cx;
-                    STORE(m, mOff + 8, x1, 8);
+                    Pack.UInt64_To_BE(x1, m[8..]);
                     x1 = cx;
                 }
                 P(nr);
-                mOff += ASCON_AEAD_RATE;
-                cOff += ASCON_AEAD_RATE;
-                clen -= ASCON_AEAD_RATE;
+                c = c[ASCON_AEAD_RATE..];
+                m = m[ASCON_AEAD_RATE..];
             }
         }
 
-        private ulong CLEAR(ulong w, int n)
-        {
-            /* undefined for n == 0 */
-            ulong mask = 0x00ffffffffffffffUL >> (n * 8 - 8);
-            return w & mask;
-        }
-
-        private void ascon_final(byte[] c, int cOff, byte[] m, int mOff, int mlen)
+        private void ascon_final(Span<byte> output, ReadOnlySpan<byte> input)
         {
             if (forEncryption)
             {
                 /* final plaintext block */
-                if (ASCON_AEAD_RATE == 16 && mlen >= 8)
+                if (ASCON_AEAD_RATE == 16 && input.Length >= 8)
                 {
-                    x0 ^= LOAD(m, mOff, 8);
-                    STORE(c, cOff, x0, 8);
-                    mOff += 8;
-                    cOff += 8;
-                    mlen -= 8;
-                    x1 ^= PAD(mlen);
-                    if (mlen != 0)
+                    x0 ^= Pack.BE_To_UInt64(input);
+                    Pack.UInt64_To_BE(x0, output);
+                    input = input[8..];
+                    output = output[8..];
+                    x1 ^= PAD(input.Length);
+                    if (!input.IsEmpty)
                     {
-                        x1 ^= LOAD(m, mOff, mlen);
-                        STORE(c, cOff, x1, mlen);
+                        x1 ^= Pack.BE_To_UInt64_High(input);
+                        Pack.UInt64_To_BE_High(x1, output[..input.Length]);
                     }
                 }
                 else
                 {
-                    x0 ^= PAD(mlen);
-                    if (mlen != 0)
+                    x0 ^= PAD(input.Length);
+                    if (!input.IsEmpty)
                     {
-                        x0 ^= LOAD(m, mOff, mlen);
-                        STORE(c, cOff, x0, mlen);
+                        x0 ^= Pack.BE_To_UInt64_High(input);
+                        Pack.UInt64_To_BE_High(x0, output[..input.Length]);
                     }
                 }
             }
             else
             {
                 /* final ciphertext block */
-                if (ASCON_AEAD_RATE == 16 && mlen >= 8)
+                if (ASCON_AEAD_RATE == 16 && input.Length >= 8)
                 {
-                    ulong cx = LOAD(m, mOff, 8);
+                    ulong cx = Pack.BE_To_UInt64(input);
                     x0 ^= cx;
-                    STORE(c, cOff, x0, 8);
+                    Pack.UInt64_To_BE(x0, output);
                     x0 = cx;
-                    mOff += 8;
-                    cOff += 8;
-                    mlen -= 8;
-                    x1 ^= PAD(mlen);
-                    if (mlen != 0)
+                    input = input[8..];
+                    output = output[8..];
+                    x1 ^= PAD(input.Length);
+                    if (!input.IsEmpty)
                     {
-                        cx = LOAD(m, mOff, mlen);
+                        cx = Pack.BE_To_UInt64_High(input);
                         x1 ^= cx;
-                        STORE(c, cOff, x1, mlen);
-                        x1 = CLEAR(x1, mlen);
+                        Pack.UInt64_To_BE_High(x1, output[..input.Length]);
+                        x1 &= ulong.MaxValue >> (input.Length << 3);
                         x1 ^= cx;
                     }
                 }
                 else
                 {
-                    x0 ^= PAD(mlen);
-                    if (mlen != 0)
+                    x0 ^= PAD(input.Length);
+                    if (!input.IsEmpty)
                     {
-                        ulong cx = LOAD(m, mOff, mlen);
+                        ulong cx = Pack.BE_To_UInt64_High(input);
                         x0 ^= cx;
-                        STORE(c, cOff, x0, mlen);
-                        x0 = CLEAR(x0, mlen);
+                        Pack.UInt64_To_BE_High(x0, output[..input.Length]);
+                        x0 &= ulong.MaxValue >> (input.Length << 3);
                         x0 ^= cx;
                     }
                 }
@@ -377,247 +599,234 @@ namespace Org.BouncyCastle.Crypto.Engines
             /* finalize */
             switch (asconParameters)
             {
-                case AsconParameters.ascon128:
-                    x1 ^= K1;
-                    x2 ^= K2;
-                    break;
-                case AsconParameters.ascon128a:
-                    x2 ^= K1;
-                    x3 ^= K2;
-                    break;
-                case AsconParameters.ascon80pq:
-                    x1 ^= KEYROT(K0, K1);
-                    x2 ^= KEYROT(K1, K2);
-                    x3 ^= KEYROT(K2, 0UL);
-                    break;
+            case AsconParameters.ascon128:
+                x1 ^= K1;
+                x2 ^= K2;
+                break;
+            case AsconParameters.ascon128a:
+                x2 ^= K1;
+                x3 ^= K2;
+                break;
+            case AsconParameters.ascon80pq:
+                x1 ^= (K0 << 32 | K1 >> 32);
+                x2 ^= (K1 << 32 | K2 >> 32);
+                x3 ^=  K2 << 32;
+                break;
             }
             P(12);
             x3 ^= K1;
             x4 ^= K2;
         }
-
-        public void Init(bool forEncryption, ICipherParameters param)
+#else
+        private int ProcessBytes(byte[] output, int outOff)
         {
-            this.forEncryption = forEncryption;
-            if (!(param is ParametersWithIV))
-            {
-                throw new ArgumentException(
-                "ASCON init parameters must include an IV");
-            }
-            ParametersWithIV ivParams = (ParametersWithIV)param;
-            byte[] npub = ivParams.GetIV();
-            if (npub == null || npub.Length != CRYPTO_ABYTES)
-            {
-                throw new ArgumentException(asconParameters + " requires exactly " + CRYPTO_ABYTES + " bytes of IV");
-            }
-            if (!(ivParams.Parameters is KeyParameter))
-            {
-                throw new ArgumentException(
-                "ASCON init parameters must include a key");
-            }
-            KeyParameter key = (KeyParameter)ivParams.Parameters;
-            byte[] k = key.GetKey();
-            if (k.Length != CRYPTO_KEYBYTES)
-            {
-                throw new ArgumentException(asconParameters + " key must be " + CRYPTO_KEYBYTES + " bytes long");
-            }
-            N0 = LOAD(npub, 0, 8);
-            N1 = LOAD(npub, 8, 8);
-            if (CRYPTO_KEYBYTES == 16)
-            {
-                K1 = LOAD(k, 0, 8);
-                K2 = LOAD(k, 8, 8);
-            }
-            else if (CRYPTO_KEYBYTES == 20)
+            int len_orig = Convert.ToInt32(message.Length);
+            int len = 0;
+            if (forEncryption)
             {
-                K0 = KEYROT(0, LOADBYTES(k, 0, 4));
-                K1 = LOADBYTES(k, 4, 8);
-                K2 = LOADBYTES(k, 12, 8);
+                if (len_orig >= ASCON_AEAD_RATE)
+                {
+                    ProcessAad();
+                    byte[] input = message.GetBuffer();
+                    len = (len_orig / ASCON_AEAD_RATE) * ASCON_AEAD_RATE;
+                    Check.OutputLength(output, outOff, len, "output buffer is too short");
+                    ascon_encrypt(output, outOff, input, 0, len);
+                    message.SetLength(0);
+                    message.Write(input, len, len_orig - len);
+                }
             }
-            initialised = true;
-            /*Mask-Gen*/
-            reset(false);
-        }
-
-        public void ProcessAadByte(byte input)
-        {
-            if (aadFinished)
+            else
             {
-                throw new ArgumentException("AAD cannot be added after reading a full block(" + ASCON_AEAD_RATE +
-                    " bytes) of input for " + (forEncryption ? "encryption" : "decryption"));
+                if (len_orig - CRYPTO_ABYTES >= ASCON_AEAD_RATE)
+                {
+                    ProcessAad();
+                    byte[] input = message.GetBuffer();
+                    len = ((len_orig - CRYPTO_ABYTES) / ASCON_AEAD_RATE) * ASCON_AEAD_RATE;
+                    Check.OutputLength(output, outOff, len, "output buffer is too short");
+                    ascon_decrypt(output, outOff, input, 0, len);
+                    message.SetLength(0);
+                    message.Write(input, len, len_orig - len);
+                }
             }
-            aadData.Write(new byte[] { input }, 0, 1);
+            return len;
         }
 
-
-        public void ProcessAadBytes(byte[] input, int inOff, int len)
+        private void ascon_adata(byte[] ad, int adOff, int adlen)
         {
-            if (aadFinished)
-            {
-                throw new ArgumentException("AAD cannot be added after reading a full block(" + ASCON_AEAD_RATE +
-                    " bytes) of input for " + (forEncryption ? "encryption" : "decryption"));
-            }
-            if ((inOff + len) > input.Length)
+            if (adlen != 0)
             {
-                throw new DataLengthException("input buffer too short");
+                /* full associated data blocks */
+                while (adlen >= ASCON_AEAD_RATE)
+                {
+                    x0 ^= Pack.BE_To_UInt64(ad, adOff);
+                    if (ASCON_AEAD_RATE == 16)
+                    {
+                        x1 ^= Pack.BE_To_UInt64(ad, adOff + 8);
+                    }
+                    P(nr);
+                    adOff += ASCON_AEAD_RATE;
+                    adlen -= ASCON_AEAD_RATE;
+                }
+                /* final associated data block */
+                if (ASCON_AEAD_RATE == 16 && adlen >= 8)
+                {
+                    x0 ^= Pack.BE_To_UInt64(ad, adOff);
+                    adOff += 8;
+                    adlen -= 8;
+                    x1 ^= PAD(adlen);
+                    if (adlen != 0)
+                    {
+                        x1 ^= Pack.BE_To_UInt64_High(ad, adOff, adlen);
+                    }
+                }
+                else
+                {
+                    x0 ^= PAD(adlen);
+                    if (adlen != 0)
+                    {
+                        x0 ^= Pack.BE_To_UInt64_High(ad, adOff, adlen);
+                    }
+                }
+                P(nr);
             }
-            aadData.Write(input, inOff, len);
-        }
-
-
-        public int ProcessByte(byte input, byte[] output, int outOff)
-        {
-            return ProcessBytes(new byte[] { input }, 0, 1, output, outOff);
+            /* domain separation */
+            x4 ^= 1UL;
         }
 
-
-        public int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
+        private void ascon_encrypt(byte[] c, int cOff, byte[] m, int mOff, int mlen)
         {
-            if (!initialised)
-            {
-                throw new ArgumentException("Need call init function before encryption/decryption");
-            }
-            if ((inOff + len) > input.Length)
+            /* full plaintext blocks */
+            while (mlen >= ASCON_AEAD_RATE)
             {
-                throw new DataLengthException("input buffer too short");
+                x0 ^= Pack.BE_To_UInt64(m, mOff);
+                Pack.UInt64_To_BE(x0, c, cOff);
+                if (ASCON_AEAD_RATE == 16)
+                {
+                    x1 ^= Pack.BE_To_UInt64(m, mOff + 8);
+                    Pack.UInt64_To_BE(x1, c, cOff + 8);
+                }
+                P(nr);
+                mOff += ASCON_AEAD_RATE;
+                cOff += ASCON_AEAD_RATE;
+                mlen -= ASCON_AEAD_RATE;
             }
-            message.Write(input, inOff, len);
-            int rv = processBytes(output, outOff);
-            encrypted = true;
-            return rv;
         }
 
-        private void processAAD()
+        private void ascon_decrypt(byte[] m, int mOff, byte[] c, int cOff, int clen)
         {
-            if (!aadFinished)
+            /* full ciphertext blocks */
+            while (clen >= ASCON_AEAD_RATE)
             {
-                byte[] ad = aadData.GetBuffer();
-                int adlen = (int)aadData.Length;
-                /* perform ascon computation */
-                ascon_adata(ad, 0, adlen);
-                aadFinished = true;
+                ulong cx = Pack.BE_To_UInt64(c, cOff);
+                x0 ^= cx;
+                Pack.UInt64_To_BE(x0, m, mOff);
+                x0 = cx;
+                if (ASCON_AEAD_RATE == 16)
+                {
+                    cx = Pack.BE_To_UInt64(c, cOff + 8);
+                    x1 ^= cx;
+                    Pack.UInt64_To_BE(x1, m, mOff + 8);
+                    x1 = cx;
+                }
+                P(nr);
+                mOff += ASCON_AEAD_RATE;
+                cOff += ASCON_AEAD_RATE;
+                clen -= ASCON_AEAD_RATE;
             }
         }
 
-        private int processBytes(byte[] output, int outOff)
+        private void ascon_final(byte[] c, int cOff, byte[] m, int mOff, int mlen)
         {
-            int len = 0;
             if (forEncryption)
             {
-                if ((int)message.Length >= ASCON_AEAD_RATE)
+                /* final plaintext block */
+                if (ASCON_AEAD_RATE == 16 && mlen >= 8)
                 {
-                    processAAD();
-                    byte[] input = message.GetBuffer();
-                    len = ((int)message.Length / ASCON_AEAD_RATE) * ASCON_AEAD_RATE;
-                    if (len + outOff > output.Length)
+                    x0 ^= Pack.BE_To_UInt64(m, mOff);
+                    Pack.UInt64_To_BE(x0, c, cOff);
+                    mOff += 8;
+                    cOff += 8;
+                    mlen -= 8;
+                    x1 ^= PAD(mlen);
+                    if (mlen != 0)
                     {
-                        throw new OutputLengthException("output buffer is too short");
+                        x1 ^= Pack.BE_To_UInt64_High(m, mOff, mlen);
+                        Pack.UInt64_To_BE_High(x1, c, cOff, mlen);
                     }
-                    ascon_encrypt(output, outOff, input, 0, len);
-                    int len_orig = (int)message.Length;
-                    message.SetLength(0);
-                    message.Write(input, len, len_orig - len);
                 }
-            }
-            else
-            {
-                if ((int)message.Length - CRYPTO_ABYTES >= ASCON_AEAD_RATE)
+                else
                 {
-                    processAAD();
-                    byte[] input = message.GetBuffer();
-                    len = (((int)message.Length - CRYPTO_ABYTES) / ASCON_AEAD_RATE) * ASCON_AEAD_RATE;
-                    if (len + outOff > output.Length)
+                    x0 ^= PAD(mlen);
+                    if (mlen != 0)
                     {
-                        throw new OutputLengthException("output buffer is too short");
+                        x0 ^= Pack.BE_To_UInt64_High(m, mOff, mlen);
+                        Pack.UInt64_To_BE_High(x0, c, cOff, mlen);
                     }
-                    ascon_decrypt(output, outOff, input, 0, len);
-                    int len_orig = (int)message.Length;
-                    message.SetLength(0);
-                    message.Write(input, len, len_orig - len);
                 }
             }
-            return len;
-        }
-
-        public int DoFinal(byte[] output, int outOff)
-        {
-            if (!initialised)
-            {
-                throw new ArgumentException("Need call init function before encryption/decryption");
-            }
-            if (!aadFinished)
-            {
-                processAAD();
-            }
-            if (!encrypted)
-            {
-                ProcessBytes(new byte[] { }, 0, 0, new byte[] { }, 0);
-            }
-            byte[] input = message.GetBuffer();
-            int len = (int)message.Length;
-            if ((forEncryption && outOff + len + CRYPTO_ABYTES > output.Length) ||
-                (!forEncryption && outOff + len - CRYPTO_ABYTES > output.Length))
-            {
-                throw new OutputLengthException("output buffer too short");
-            }
-            if (forEncryption)
-            {
-                ascon_final(output, outOff, input, 0, len);
-                /* set tag */
-                mac = new byte[16];
-                STOREBYTES(mac, 0, x3, 8);
-                STOREBYTES(mac, 8, x4, 8);
-                Array.Copy(mac, 0, output, len + outOff, 16);
-                reset(false);
-                return len + CRYPTO_ABYTES;
-            }
             else
             {
-                len -= CRYPTO_ABYTES;
-                ascon_final(output, outOff, input, 0, len);
-                x3 ^= LOADBYTES(input, len, 8);
-                x4 ^= LOADBYTES(input, len + 8, 8);
-                ulong result = x3 | x4;
-                result |= result >> 32;
-                result |= result >> 16;
-                result |= result >> 8;
-                reset(true);
-                if ((((((int)(result & 0xffUL) - 1) >> 8) & 1) - 1) != 0)
+                /* final ciphertext block */
+                if (ASCON_AEAD_RATE == 16 && mlen >= 8)
                 {
-                    throw new ArgumentException("Mac does not match");
+                    ulong cx = Pack.BE_To_UInt64(m, mOff);
+                    x0 ^= cx;
+                    Pack.UInt64_To_BE(x0, c, cOff);
+                    x0 = cx;
+                    mOff += 8;
+                    cOff += 8;
+                    mlen -= 8;
+                    x1 ^= PAD(mlen);
+                    if (mlen != 0)
+                    {
+                        cx = Pack.BE_To_UInt64_High(m, mOff, mlen);
+                        x1 ^= cx;
+                        Pack.UInt64_To_BE_High(x1, c, cOff, mlen);
+                        x1 &= ulong.MaxValue >> (mlen << 3);
+                        x1 ^= cx;
+                    }
+                }
+                else
+                {
+                    x0 ^= PAD(mlen);
+                    if (mlen != 0)
+                    {
+                        ulong cx = Pack.BE_To_UInt64_High(m, mOff, mlen);
+                        x0 ^= cx;
+                        Pack.UInt64_To_BE_High(x0, c, cOff, mlen);
+                        x0 &= ulong.MaxValue >> (mlen << 3);
+                        x0 ^= cx;
+                    }
                 }
-                return len;
             }
+            /* finalize */
+            switch (asconParameters)
+            {
+            case AsconParameters.ascon128:
+                x1 ^= K1;
+                x2 ^= K2;
+                break;
+            case AsconParameters.ascon128a:
+                x2 ^= K1;
+                x3 ^= K2;
+                break;
+            case AsconParameters.ascon80pq:
+                x1 ^= (K0 << 32 | K1 >> 32);
+                x2 ^= (K1 << 32 | K2 >> 32);
+                x3 ^=  K2 << 32;
+                break;
+            }
+            P(12);
+            x3 ^= K1;
+            x4 ^= K2;
         }
+#endif
 
-
-        public byte[] GetMac()
-        {
-            return mac;
-        }
-
-
-        public int GetUpdateOutputSize(int len)
-        {
-            return len;
-        }
-
-        public int GetOutputSize(int len)
-        {
-            return len + CRYPTO_ABYTES;
-        }
-
-        public void Reset()
-        {
-            reset(true);
-        }
-
-        private void reset(bool clearMac)
+        private void Reset(bool clearMac)
         {
             if (!initialised)
-            {
                 throw new ArgumentException("Need call init function before encryption/decryption");
-            }
+
             x0 = x1 = x2 = x3 = x4 = 0;
             ascon_aeadinit();
             aadData.SetLength(0);
@@ -630,64 +839,9 @@ namespace Org.BouncyCastle.Crypto.Engines
             }
         }
 
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-        public void ProcessAadBytes(ReadOnlySpan<byte> input)
+        private static ulong PAD(int i)
         {
-            if (aadFinished)
-            {
-                throw new ArgumentException("AAD cannot be added after reading a full block(" + ASCON_AEAD_RATE +
-                    " bytes) of input for " + (forEncryption ? "encryption" : "decryption"));
-            }
-            aadData.Write(input);
-        }
-
-        public int ProcessByte(byte input, Span<byte> output)
-        {
-            byte[] rv = new byte[1];
-            int len = ProcessBytes(new byte[] { input }, 0, 1, rv, 0);
-            rv.AsSpan(0, len).CopyTo(output);
-            return len;
-        }
-
-        public int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
-        {
-            byte[] rv = new byte[input.Length];
-            int len = ProcessBytes(input.ToArray(), 0, rv.Length, rv, 0);
-            rv.AsSpan(0, len).CopyTo(output);
-            return len;
-        }
-
-        public int DoFinal(Span<byte> output)
-        {
-            byte[] rv;
-            if (forEncryption)
-            {
-                rv = new byte[message.Length + 16];
-            }
-            else
-            {
-                rv = new byte[message.Length];
-            }
-            int len = DoFinal(rv, 0);
-            rv.AsSpan(0, len).CopyTo(output);
-            return rv.Length;
-        }
-#endif
-        public int GetBlockSize()
-        {
-            return ASCON_AEAD_RATE;
-        }
-
-        public int GetKeyBytesSize()
-        {
-            return CRYPTO_KEYBYTES;
-        }
-
-        public int GetIVBytesSize()
-        {
-            return CRYPTO_ABYTES;
+            return 0x8000000000000000UL >> (i << 3);
         }
     }
 }
-
-
diff --git a/crypto/test/src/crypto/test/AsconTest.cs b/crypto/test/src/crypto/test/AsconTest.cs
index 837dc413b..eca949471 100644
--- a/crypto/test/src/crypto/test/AsconTest.cs
+++ b/crypto/test/src/crypto/test/AsconTest.cs
@@ -5,7 +5,6 @@ using System.IO;
 using NUnit.Framework;
 
 using Org.BouncyCastle.Crypto.Engines;
-using Org.BouncyCastle.Crypto.Modes;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.Encoders;
@@ -14,87 +13,82 @@ using Org.BouncyCastle.Utilities.Test;
 namespace Org.BouncyCastle.Crypto.Tests
 {
     [TestFixture]
-    public class AsconTest : SimpleTest
+    public class AsconTest
+        : SimpleTest
     {
-        public override string Name
-        {
-            get { return "ASCON AEAD"; }
-        }
+        public override string Name => "ASCON AEAD";
 
         [Test]
         public override void PerformTest()
         {
-            AsconEngine Ascon = new AsconEngine(AsconEngine.AsconParameters.ascon80pq);
-            testExceptions(Ascon, Ascon.GetKeyBytesSize(), Ascon.GetIVBytesSize(), Ascon.GetBlockSize());
-            testParameters(Ascon, 20, 16, 16, 8);
-            Ascon = new AsconEngine(AsconEngine.AsconParameters.ascon128a);
-            testExceptions(Ascon, Ascon.GetKeyBytesSize(), Ascon.GetIVBytesSize(), Ascon.GetBlockSize());
-            testParameters(Ascon, 16, 16, 16, 16);
-            Ascon = new AsconEngine(AsconEngine.AsconParameters.ascon128);
-            testExceptions(Ascon, Ascon.GetKeyBytesSize(), Ascon.GetIVBytesSize(), Ascon.GetBlockSize());
-            testParameters(Ascon, 16, 16, 16, 8);
-            testVectors(AsconEngine.AsconParameters.ascon80pq, "160_128");
-            testVectors(AsconEngine.AsconParameters.ascon128a, "128_128_a");
-            testVectors(AsconEngine.AsconParameters.ascon128, "128_128");
+            AsconEngine asconEngine = new AsconEngine(AsconEngine.AsconParameters.ascon80pq);
+            ImplTestExceptions(asconEngine);
+            ImplTestParameters(asconEngine, 20, 16, 16);
+
+            asconEngine = new AsconEngine(AsconEngine.AsconParameters.ascon128a);
+            ImplTestExceptions(asconEngine);
+            ImplTestParameters(asconEngine, 16, 16, 16);
+
+            asconEngine = new AsconEngine(AsconEngine.AsconParameters.ascon128);
+            ImplTestExceptions(asconEngine);
+            ImplTestParameters(asconEngine, 16, 16, 16);
+
+            ImplTestVectors(AsconEngine.AsconParameters.ascon80pq, "160_128");
+            ImplTestVectors(AsconEngine.AsconParameters.ascon128a, "128_128_a");
+            ImplTestVectors(AsconEngine.AsconParameters.ascon128, "128_128");
         }
 
-        private void testVectors(AsconEngine.AsconParameters asconParameters, string filename)
+        private void ImplTestVectors(AsconEngine.AsconParameters asconParameters, string filename)
         {
+            Random random = new Random();
             AsconEngine Ascon = new AsconEngine(asconParameters);
-            ICipherParameters param;
             var buf = new Dictionary<string, string>();
             //TestSampler sampler = new TestSampler();
             using (var src = new StreamReader(SimpleTest.GetTestDataAsStream("crypto.ascon.LWC_AEAD_KAT_" + filename + ".txt")))
             {
-                string line;
-                string[] data;
-                byte[] rv;
                 Dictionary<string, string> map = new Dictionary<string, string>();
+                string line;
                 while ((line = src.ReadLine()) != null)
                 {
-                    data = line.Split(' ');
+                    var data = line.Split(' ');
                     if (data.Length == 1)
                     {
-                        //if (!map["Count"].Equals("265"))
-                        //{
-                        //    continue;
-                        //}
                         byte[] key = Hex.Decode(map["Key"]);
                         byte[] nonce = Hex.Decode(map["Nonce"]);
                         byte[] ad = Hex.Decode(map["AD"]);
                         byte[] pt = Hex.Decode(map["PT"]);
                         byte[] ct = Hex.Decode(map["CT"]);
-                        param = new ParametersWithIV(new KeyParameter(key), nonce);
-                        Ascon.Init(true, param);
-                        Ascon.ProcessAadBytes(ad, 0, ad.Length);
-                        rv = new byte[Ascon.GetOutputSize(pt.Length)];
-                        int len = Ascon.ProcessBytes(pt, 0, pt.Length, rv, 0);
-                        //byte[] mac = new byte[16];
-                        Ascon.DoFinal(rv, len);
-                        //foreach(byte b in Hex.Decode(map["CT"]))
-                        //{
-                        //    Console.Write(b.ToString("X2"));
-                        //}
-                        //Console.WriteLine();
-                        //foreach (byte b in Arrays.Concatenate(rv, mac))
-                        //{
-                        //    Console.Write(b.ToString("X2"));
-                        //}
-                        //Console.WriteLine();
-                        Assert.True(Arrays.AreEqual(rv, ct));
-                        Ascon.Reset();
-                        Ascon.Init(false, param);
-                        //Decrypt
-                        Ascon.ProcessAadBytes(ad, 0, ad.Length);
-                        rv = new byte[pt.Length + 16];
-                        len = Ascon.ProcessBytes(ct, 0, ct.Length, rv, 0);
-                        Ascon.DoFinal(rv, len);
-                        byte[] pt_recovered = new byte[pt.Length];
-                        Array.Copy(rv, 0, pt_recovered, 0, pt.Length);
-                        Assert.True(Arrays.AreEqual(pt, pt_recovered));
-                        //Console.WriteLine(map["Count"] + " pass");
                         map.Clear();
 
+                        var param = new ParametersWithIV(new KeyParameter(key), nonce);
+
+                        // Encrypt
+                        {
+                            Ascon.Init(true, param);
+
+                            var rv = new byte[Ascon.GetOutputSize(pt.Length)];
+                            random.NextBytes(rv); // should overwrite any existing data
+
+                            Ascon.ProcessAadBytes(ad, 0, ad.Length);
+                            int len = Ascon.ProcessBytes(pt, 0, pt.Length, rv, 0);
+                            len += Ascon.DoFinal(rv, len);
+
+                            Assert.True(Arrays.AreEqual(rv, 0, len, ct, 0, ct.Length));
+                        }
+
+                        // Decrypt
+                        {
+                            Ascon.Init(false, param);
+
+                            var rv = new byte[Ascon.GetOutputSize(ct.Length)];
+                            random.NextBytes(rv); // should overwrite any existing data
+
+                            Ascon.ProcessAadBytes(ad, 0, ad.Length);
+                            int len = Ascon.ProcessBytes(ct, 0, ct.Length, rv, 0);
+                            len += Ascon.DoFinal(rv, len);
+
+                            Assert.True(Arrays.AreEqual(rv, 0, len, pt, 0, pt.Length));
+                        }
                     }
                     else
                     {
@@ -106,27 +100,23 @@ namespace Org.BouncyCastle.Crypto.Tests
                         {
                             map[data[0].Trim()] = "";
                         }
-
                     }
                 }
             }
-            Console.WriteLine("Ascon AEAD pass");
         }
 
-
-        private void testExceptions(IAeadBlockCipher aeadBlockCipher, int keysize, int ivsize, int blocksize)
-
+        private void ImplTestExceptions(AsconEngine asconEngine)
         {
-            ICipherParameters param;
-            byte[] k = new byte[keysize];
-            byte[] iv = new byte[ivsize];
+            int keySize = asconEngine.GetKeyBytesSize(), ivSize = asconEngine.GetIVBytesSize();
+            byte[] k = new byte[keySize];
+            byte[] iv = new byte[ivSize];
             byte[] m = new byte[0];
-            byte[] c1 = new byte[aeadBlockCipher.GetOutputSize(m.Length)];
-            param = new ParametersWithIV(new KeyParameter(k), iv);
+            byte[] c1 = new byte[asconEngine.GetOutputSize(m.Length)];
+            var param = new ParametersWithIV(new KeyParameter(k), iv);
             try
             {
-                aeadBlockCipher.ProcessBytes(m, 0, m.Length, c1, 0);
-                Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialed before ProcessBytes");
+                asconEngine.ProcessBytes(m, 0, m.Length, c1, 0);
+                Assert.Fail(asconEngine.AlgorithmName + " need to be initialed before ProcessBytes");
             }
             catch (ArgumentException)
             {
@@ -135,8 +125,8 @@ namespace Org.BouncyCastle.Crypto.Tests
 
             try
             {
-                aeadBlockCipher.ProcessByte((byte)0, c1, 0);
-                Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialed before ProcessByte");
+                asconEngine.ProcessByte((byte)0, c1, 0);
+                Assert.Fail(asconEngine.AlgorithmName + " need to be initialed before ProcessByte");
             }
             catch (ArgumentException)
             {
@@ -145,8 +135,8 @@ namespace Org.BouncyCastle.Crypto.Tests
 
             try
             {
-                aeadBlockCipher.Reset();
-                Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialed before reset");
+                asconEngine.Reset();
+                Assert.Fail(asconEngine.AlgorithmName + " need to be initialed before reset");
             }
             catch (ArgumentException)
             {
@@ -155,8 +145,8 @@ namespace Org.BouncyCastle.Crypto.Tests
 
             try
             {
-                aeadBlockCipher.DoFinal(c1, m.Length);
-                Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialed before dofinal");
+                asconEngine.DoFinal(c1, m.Length);
+                Assert.Fail(asconEngine.AlgorithmName + " need to be initialed before dofinal");
             }
             catch (ArgumentException)
             {
@@ -165,25 +155,26 @@ namespace Org.BouncyCastle.Crypto.Tests
 
             try
             {
-                aeadBlockCipher.GetMac();
-                aeadBlockCipher.GetOutputSize(0);
-                aeadBlockCipher.GetUpdateOutputSize(0);
+                asconEngine.GetMac();
+                asconEngine.GetOutputSize(0);
+                asconEngine.GetUpdateOutputSize(0);
             }
             catch (ArgumentException)
             {
                 //expected
-                Assert.Fail(aeadBlockCipher.AlgorithmName + " functions can be called before initialisation");
+                Assert.Fail(asconEngine.AlgorithmName + " functions can be called before initialisation");
             }
+
             Random rand = new Random();
             int randomNum;
-            while ((randomNum = rand.Next(100)) == keysize) ;
+            while ((randomNum = rand.Next(100)) == keySize) ;
             byte[] k1 = new byte[randomNum];
-            while ((randomNum = rand.Next(100)) == ivsize) ;
+            while ((randomNum = rand.Next(100)) == ivSize) ;
             byte[] iv1 = new byte[randomNum];
             try
             {
-                aeadBlockCipher.Init(true, new ParametersWithIV(new KeyParameter(k1), iv));
-                Assert.Fail(aeadBlockCipher.AlgorithmName + " k size does not match");
+                asconEngine.Init(true, new ParametersWithIV(new KeyParameter(k1), iv));
+                Assert.Fail(asconEngine.AlgorithmName + " k size does not match");
             }
             catch (ArgumentException)
             {
@@ -191,25 +182,24 @@ namespace Org.BouncyCastle.Crypto.Tests
             }
             try
             {
-                aeadBlockCipher.Init(true, new ParametersWithIV(new KeyParameter(k), iv1));
-                Assert.Fail(aeadBlockCipher.AlgorithmName + "iv size does not match");
+                asconEngine.Init(true, new ParametersWithIV(new KeyParameter(k), iv1));
+                Assert.Fail(asconEngine.AlgorithmName + "iv size does not match");
             }
             catch (ArgumentException)
             {
                 //expected
             }
 
-
-            aeadBlockCipher.Init(true, param);
+            asconEngine.Init(true, param);
             try
             {
-                aeadBlockCipher.DoFinal(c1, m.Length);
+                asconEngine.DoFinal(c1, m.Length);
             }
             catch (Exception)
             {
-                Assert.Fail(aeadBlockCipher.AlgorithmName + " allows no input for AAD and plaintext");
+                Assert.Fail(asconEngine.AlgorithmName + " allows no input for AAD and plaintext");
             }
-            byte[] mac2 = aeadBlockCipher.GetMac();
+            byte[] mac2 = asconEngine.GetMac();
             if (mac2 == null)
             {
                 Assert.Fail("mac should not be empty after dofinal");
@@ -218,18 +208,18 @@ namespace Org.BouncyCastle.Crypto.Tests
             {
                 Assert.Fail("mac should be equal when calling dofinal and getMac");
             }
-            aeadBlockCipher.ProcessAadByte((byte)0);
-            byte[] mac1 = new byte[aeadBlockCipher.GetOutputSize(0)];
-            aeadBlockCipher.DoFinal(mac1, 0);
+            asconEngine.ProcessAadByte((byte)0);
+            byte[] mac1 = new byte[asconEngine.GetOutputSize(0)];
+            asconEngine.DoFinal(mac1, 0);
             if (Arrays.AreEqual(mac1, mac2))
             {
                 Assert.Fail("mac should not match");
             }
-            aeadBlockCipher.Reset();
-            aeadBlockCipher.ProcessBytes(new byte[16], 0, 16, new byte[16], 0);
+            asconEngine.Reset();
+            asconEngine.ProcessBytes(new byte[16], 0, 16, new byte[16], 0);
             try
             {
-                aeadBlockCipher.ProcessAadByte((byte)0);
+                asconEngine.ProcessAadByte((byte)0);
                 Assert.Fail("ProcessAadByte(s) cannot be called after encryption/decryption");
             }
             catch (ArgumentException)
@@ -238,7 +228,7 @@ namespace Org.BouncyCastle.Crypto.Tests
             }
             try
             {
-                aeadBlockCipher.ProcessAadBytes(new byte[] { 0 }, 0, 1);
+                asconEngine.ProcessAadBytes(new byte[] { 0 }, 0, 1);
                 Assert.Fail("ProcessAadByte(s) cannot be called once only");
             }
             catch (ArgumentException)
@@ -246,10 +236,10 @@ namespace Org.BouncyCastle.Crypto.Tests
                 //expected
             }
 
-            aeadBlockCipher.Reset();
+            asconEngine.Reset();
             try
             {
-                aeadBlockCipher.ProcessAadBytes(new byte[] { 0 }, 1, 1);
+                asconEngine.ProcessAadBytes(new byte[] { 0 }, 1, 1);
                 Assert.Fail("input for ProcessAadBytes is too short");
             }
             catch (DataLengthException)
@@ -258,7 +248,7 @@ namespace Org.BouncyCastle.Crypto.Tests
             }
             try
             {
-                aeadBlockCipher.ProcessBytes(new byte[] { 0 }, 1, 1, c1, 0);
+                asconEngine.ProcessBytes(new byte[] { 0 }, 1, 1, c1, 0);
                 Assert.Fail("input for ProcessBytes is too short");
             }
             catch (DataLengthException)
@@ -267,7 +257,7 @@ namespace Org.BouncyCastle.Crypto.Tests
             }
             try
             {
-                aeadBlockCipher.ProcessBytes(new byte[16], 0, 16, new byte[16], 8);
+                asconEngine.ProcessBytes(new byte[16], 0, 16, new byte[16], 8);
                 Assert.Fail("output for ProcessBytes is too short");
             }
             catch (OutputLengthException)
@@ -276,7 +266,7 @@ namespace Org.BouncyCastle.Crypto.Tests
             }
             try
             {
-                aeadBlockCipher.DoFinal(new byte[2], 2);
+                asconEngine.DoFinal(new byte[2], 2);
                 Assert.Fail("output for dofinal is too short");
             }
             catch (DataLengthException)
@@ -284,59 +274,58 @@ namespace Org.BouncyCastle.Crypto.Tests
                 //expected
             }
 
-            mac1 = new byte[aeadBlockCipher.GetOutputSize(0)];
-            mac2 = new byte[aeadBlockCipher.GetOutputSize(0)];
-            aeadBlockCipher.Reset();
-            aeadBlockCipher.ProcessAadBytes(new byte[] { 0, 0 }, 0, 2);
-            aeadBlockCipher.DoFinal(mac1, 0);
-            aeadBlockCipher.Reset();
-            aeadBlockCipher.ProcessAadByte((byte)0);
-            aeadBlockCipher.ProcessAadByte((byte)0);
-            aeadBlockCipher.DoFinal(mac2, 0);
+            mac1 = new byte[asconEngine.GetOutputSize(0)];
+            mac2 = new byte[asconEngine.GetOutputSize(0)];
+            asconEngine.Reset();
+            asconEngine.ProcessAadBytes(new byte[] { 0, 0 }, 0, 2);
+            asconEngine.DoFinal(mac1, 0);
+            asconEngine.Reset();
+            asconEngine.ProcessAadByte((byte)0);
+            asconEngine.ProcessAadByte((byte)0);
+            asconEngine.DoFinal(mac2, 0);
             if (!Arrays.AreEqual(mac1, mac2))
             {
                 Assert.Fail("mac should match for the same AAD with different ways of inputing");
             }
 
-            byte[] c2 = new byte[aeadBlockCipher.GetOutputSize(10)];
-            byte[] c3 = new byte[aeadBlockCipher.GetOutputSize(10) + 2];
+            byte[] c2 = new byte[asconEngine.GetOutputSize(10)];
+            byte[] c3 = new byte[asconEngine.GetOutputSize(10) + 2];
             byte[] aad2 = { 0, 1, 2, 3, 4 };
             byte[] aad3 = { 0, 0, 1, 2, 3, 4, 5 };
             byte[] m2 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
             byte[] m3 = { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
             byte[] m4 = new byte[m2.Length];
-            aeadBlockCipher.Reset();
-            aeadBlockCipher.ProcessAadBytes(aad2, 0, aad2.Length);
-            int offset = aeadBlockCipher.ProcessBytes(m2, 0, m2.Length, c2, 0);
-            aeadBlockCipher.DoFinal(c2, offset);
-            aeadBlockCipher.Reset();
-            aeadBlockCipher.ProcessAadBytes(aad3, 1, aad2.Length);
-            offset = aeadBlockCipher.ProcessBytes(m3, 1, m2.Length, c3, 1);
-            aeadBlockCipher.DoFinal(c3, offset + 1);
+            asconEngine.Reset();
+            asconEngine.ProcessAadBytes(aad2, 0, aad2.Length);
+            int offset = asconEngine.ProcessBytes(m2, 0, m2.Length, c2, 0);
+            asconEngine.DoFinal(c2, offset);
+            asconEngine.Reset();
+            asconEngine.ProcessAadBytes(aad3, 1, aad2.Length);
+            offset = asconEngine.ProcessBytes(m3, 1, m2.Length, c3, 1);
+            asconEngine.DoFinal(c3, offset + 1);
             byte[] c3_partial = new byte[c2.Length];
             Array.Copy(c3, 1, c3_partial, 0, c2.Length);
             if (!Arrays.AreEqual(c2, c3_partial))
             {
                 Assert.Fail("mac should match for the same AAD and message with different offset for both input and output");
             }
-            aeadBlockCipher.Reset();
-            aeadBlockCipher.Init(false, param);
-            aeadBlockCipher.ProcessAadBytes(aad2, 0, aad2.Length);
-            offset = aeadBlockCipher.ProcessBytes(c2, 0, c2.Length, m4, 0);
-            aeadBlockCipher.DoFinal(m4, offset);
+            asconEngine.Reset();
+            asconEngine.Init(false, param);
+            asconEngine.ProcessAadBytes(aad2, 0, aad2.Length);
+            offset = asconEngine.ProcessBytes(c2, 0, c2.Length, m4, 0);
+            asconEngine.DoFinal(m4, offset);
             if (!Arrays.AreEqual(m2, m4))
             {
                 Assert.Fail("The encryption and decryption does not recover the plaintext");
             }
-            Console.WriteLine(aeadBlockCipher.AlgorithmName + " test Exceptions pass");
             c2[c2.Length - 1] ^= 1;
-            aeadBlockCipher.Reset();
-            aeadBlockCipher.Init(false, param);
-            aeadBlockCipher.ProcessAadBytes(aad2, 0, aad2.Length);
-            offset = aeadBlockCipher.ProcessBytes(c2, 0, c2.Length, m4, 0);
+            asconEngine.Reset();
+            asconEngine.Init(false, param);
+            asconEngine.ProcessAadBytes(aad2, 0, aad2.Length);
+            offset = asconEngine.ProcessBytes(c2, 0, c2.Length, m4, 0);
             try
             {
-                aeadBlockCipher.DoFinal(m4, offset);
+                asconEngine.DoFinal(m4, offset);
                 Assert.Fail("The decryption should fail");
             }
             catch (ArgumentException)
@@ -345,29 +334,26 @@ namespace Org.BouncyCastle.Crypto.Tests
             }
             c2[c2.Length - 1] ^= 1;
 
-            byte[] m7 = new byte[blocksize * 2];
-            for (int i = 0; i < m7.Length; ++i)
-            {
-                m7[i] = (byte)rand.Next();
-            }
-            byte[] c7 = new byte[aeadBlockCipher.GetOutputSize(m7.Length)];
+            byte[] m7 = new byte[32 + rand.Next(16)];
+            rand.NextBytes(m7);
+
+            byte[] c7 = new byte[asconEngine.GetOutputSize(m7.Length)];
             byte[] c8 = new byte[c7.Length];
             byte[] c9 = new byte[c7.Length];
-            aeadBlockCipher.Init(true, param);
-            aeadBlockCipher.ProcessAadBytes(aad2, 0, aad2.Length);
-            offset = aeadBlockCipher.ProcessBytes(m7, 0, m7.Length, c7, 0);
-            aeadBlockCipher.DoFinal(c7, offset);
-            aeadBlockCipher.Reset();
-            aeadBlockCipher.ProcessAadBytes(aad2, 0, aad2.Length);
-            offset = aeadBlockCipher.ProcessBytes(m7, 0, blocksize, c8, 0);
-            offset += aeadBlockCipher.ProcessBytes(m7, blocksize, m7.Length - blocksize, c8, offset);
-            aeadBlockCipher.DoFinal(c8, offset);
-            aeadBlockCipher.Reset();
-            int split = rand.Next(blocksize * 2);
-            aeadBlockCipher.ProcessAadBytes(aad2, 0, aad2.Length);
-            offset = aeadBlockCipher.ProcessBytes(m7, 0, split, c9, 0);
-            offset += aeadBlockCipher.ProcessBytes(m7, split, m7.Length - split, c9, offset);
-            aeadBlockCipher.DoFinal(c9, offset);
+            asconEngine.Init(true, param);
+            asconEngine.ProcessAadBytes(aad2, 0, aad2.Length);
+            offset = asconEngine.ProcessBytes(m7, 0, m7.Length, c7, 0);
+            asconEngine.DoFinal(c7, offset);
+            asconEngine.Reset();
+            asconEngine.ProcessAadBytes(aad2, 0, aad2.Length);
+            offset = asconEngine.ProcessBytes(m7, 0, m7.Length, c8, 0);
+            offset += asconEngine.DoFinal(c8, offset);
+            asconEngine.Reset();
+            int split = rand.Next(1, m7.Length);
+            asconEngine.ProcessAadBytes(aad2, 0, aad2.Length);
+            offset = asconEngine.ProcessBytes(m7, 0, split, c9, 0);
+            offset += asconEngine.ProcessBytes(m7, split, m7.Length - split, c9, offset);
+            asconEngine.DoFinal(c9, offset);
             if (!Arrays.AreEqual(c7, c8) || !Arrays.AreEqual(c7, c9))
             {
                 Assert.Fail("Splitting input of plaintext should output the same ciphertext");
@@ -379,10 +365,10 @@ namespace Org.BouncyCastle.Crypto.Tests
             Span<byte> c4_2 = new byte[c2.Length];
             ReadOnlySpan<byte> m5 = new ReadOnlySpan<byte>(m2);
             ReadOnlySpan<byte> aad4 = new ReadOnlySpan<byte>(aad2);
-            aeadBlockCipher.Init(true, param);
-            aeadBlockCipher.ProcessAadBytes(aad4);
-            offset = aeadBlockCipher.ProcessBytes(m5, c4_1);
-            aeadBlockCipher.DoFinal(c4_2);
+            asconEngine.Init(true, param);
+            asconEngine.ProcessAadBytes(aad4);
+            offset = asconEngine.ProcessBytes(m5, c4_1);
+            asconEngine.DoFinal(c4_2);
             byte[] c5 = new byte[c2.Length];
             Array.Copy(c4_1.ToArray(), 0, c5, 0, offset);
             Array.Copy(c4_2.ToArray(), 0, c5, offset, c5.Length - offset);
@@ -390,14 +376,14 @@ namespace Org.BouncyCastle.Crypto.Tests
             {
                 Assert.Fail("mac should match for the same AAD and message with different offset for both input and output");
             }
-            aeadBlockCipher.Reset();
-            aeadBlockCipher.Init(false, param);
+            asconEngine.Reset();
+            asconEngine.Init(false, param);
             Span<byte> m6_1 = new byte[m2.Length];
             Span<byte> m6_2 = new byte[m2.Length];
             ReadOnlySpan<byte> c6 = new ReadOnlySpan<byte>(c2);
-            aeadBlockCipher.ProcessAadBytes(aad4);
-            offset = aeadBlockCipher.ProcessBytes(c6, m6_1);
-            aeadBlockCipher.DoFinal(m6_2);
+            asconEngine.ProcessAadBytes(aad4);
+            offset = asconEngine.ProcessBytes(c6, m6_1);
+            asconEngine.DoFinal(m6_2);
             byte[] m6 = new byte[m2.Length];
             Array.Copy(m6_1.ToArray(), 0, m6, 0, offset);
             Array.Copy(m6_2.ToArray(), 0, m6, offset, m6.Length - offset);
@@ -408,25 +394,14 @@ namespace Org.BouncyCastle.Crypto.Tests
 #endif
         }
 
-        private void testParameters(AsconEngine ascon, int keySize, int ivSize, int macSize, int blockSize)
+        private void ImplTestParameters(AsconEngine asconEngine, int keySize, int ivSize, int macSize)
         {
-            if (ascon.GetKeyBytesSize() != keySize)
-            {
-                Assert.Fail("key bytes of " + ascon.AlgorithmName + " is not correct");
-            }
-            if (ascon.GetIVBytesSize() != ivSize)
-            {
-                Assert.Fail("iv bytes of " + ascon.AlgorithmName + " is not correct");
-            }
-            if (ascon.GetOutputSize(0) != macSize)
-            {
-                Assert.Fail("mac bytes of " + ascon.AlgorithmName + " is not correct");
-            }
-            if (ascon.GetBlockSize() != blockSize)
-            {
-                Assert.Fail("block size of " + ascon.AlgorithmName + " is not correct");
-            }
-            Console.WriteLine(ascon.AlgorithmName + " test Parameters pass");
+            Assert.AreEqual(keySize, asconEngine.GetKeyBytesSize(),
+                "key bytes of " + asconEngine.AlgorithmName + " is not correct");
+            Assert.AreEqual(ivSize, asconEngine.GetIVBytesSize(),
+                "iv bytes of " + asconEngine.AlgorithmName + " is not correct");
+            Assert.AreEqual(macSize, asconEngine.GetOutputSize(0),
+                "mac bytes of " + asconEngine.AlgorithmName + " is not correct");
         }
     }
-}
\ No newline at end of file
+}