summary refs log tree commit diff
diff options
context:
space:
mode:
authorgefeili <gli@keyfactor.com>2023-01-31 15:59:37 +1030
committergefeili <gli@keyfactor.com>2023-01-31 15:59:37 +1030
commit70473a06799b0f5273b61dcba2f9057b2a8bdf68 (patch)
tree297339cd77f39439ef67ab7d1d95d05fbad5639d
parentInitial push of Elephant v2 (diff)
downloadBouncyCastle.NET-ed25519-70473a06799b0f5273b61dcba2f9057b2a8bdf68.tar.xz
Add test of Elephyant and decryption part
-rw-r--r--crypto/src/crypto/engines/ElephantEngine.cs181
-rw-r--r--crypto/test/src/crypto/test/ElephantTest.cs360
2 files changed, 469 insertions, 72 deletions
diff --git a/crypto/src/crypto/engines/ElephantEngine.cs b/crypto/src/crypto/engines/ElephantEngine.cs
index b0ec4733c..e3d9ca670 100644
--- a/crypto/src/crypto/engines/ElephantEngine.cs
+++ b/crypto/src/crypto/engines/ElephantEngine.cs
@@ -12,7 +12,7 @@ using Org.BouncyCastle.Utilities;
  */
 namespace Org.BouncyCastle.Crypto.Engines
 {
-    public class ElephantEngine : IAeadCipher
+    public class ElephantEngine : IAeadBlockCipher
     {
         public enum ElephantParameters
         {
@@ -20,23 +20,23 @@ namespace Org.BouncyCastle.Crypto.Engines
             elephant176,
             elephant200
         }
-
+        private bool forEncryption;
+        private readonly string algorithmName;
         private ElephantParameters parameters;
         private int BLOCK_SIZE;
         private int nBits;
         private int nSBox;
         private int nRounds;
         private byte lfsrIV;
-        private byte[] m;
         private byte[] npub;
         private byte[] expanded_key;
-        private byte[] tag_buffer;
+        private byte[] tag;
         private byte CRYPTO_KEYBYTES = 16;
         private byte CRYPTO_NPUBBYTES = 12;
         private byte CRYPTO_ABYTES;
+        private bool initialised;
         private MemoryStream aadData = new MemoryStream();
-        private bool encrypted;
-        private bool aadFinished;
+        private MemoryStream message = new MemoryStream();
 
         private readonly byte[] sBoxLayer = {
         (byte)0xee, (byte)0xed, (byte)0xeb, (byte)0xe0, (byte)0xe2, (byte)0xe1, (byte)0xe4, (byte)0xef, (byte)0xe7, (byte)0xea, (byte)0xe8, (byte)0xe5, (byte)0xe9, (byte)0xec, (byte)0xe3, (byte)0xe6,
@@ -75,6 +75,7 @@ namespace Org.BouncyCastle.Crypto.Engines
                     nRounds = 80;
                     lfsrIV = 0x75;
                     CRYPTO_ABYTES = 8;
+                    algorithmName = "Elephant 160 AEAD";
                     break;
                 case ElephantParameters.elephant176:
                     BLOCK_SIZE = 22;
@@ -83,15 +84,19 @@ namespace Org.BouncyCastle.Crypto.Engines
                     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;
-            tag_buffer = new byte[BLOCK_SIZE];
+            initialised = false;
             reset(false);
         }
 
@@ -157,7 +162,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return x + y * 5;
         }
 
-        void KeccakP200Round(byte[] state, int indexRound)
+        private void KeccakP200Round(byte[] state, int indexRound)
         {
             int x, y;
             byte[] tempA = new byte[25];
@@ -215,7 +220,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 
         // State should be BLOCK_SIZE bytes long
         // Note: input may be equal to output
-        void lfsr_step(byte[] output, byte[] input)
+        private void lfsr_step(byte[] output, byte[] input)
         {
             switch (parameters)
             {
@@ -233,7 +238,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             Array.Copy(input, 1, output, 0, BLOCK_SIZE - 1);
         }
 
-        void xor_block(byte[] state, byte[] block, int bOff, int size)
+        private void xor_block(byte[] state, byte[] block, int bOff, int size)
         {
             for (int i = 0; i < size; ++i)
             {
@@ -244,7 +249,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         // Write the ith assocated data block to "output".
         // The nonce is prepended and padding is added as required.
         // adlen is the length of the associated data in bytes
-        void get_ad_block(byte[] output, byte[] ad, int adlen, byte[] npub, int i)
+        private void get_ad_block(byte[] output, byte[] ad, int adlen, byte[] npub, int i)
         {
             int len = 0;
             // First block contains nonce
@@ -282,9 +287,9 @@ namespace Org.BouncyCastle.Crypto.Engines
 
         // Return the ith ciphertext block.
         // clen is the length of the ciphertext in bytes
-        void get_c_block(byte[] output, byte[] c, int cOff, int clen, int i)
+        private void get_c_block(byte[] output, byte[] c, int cOff, int clen, int i)
         {
-            int block_offset = cOff + i * BLOCK_SIZE;
+            int block_offset = i * BLOCK_SIZE;
             // If clen is divisible by BLOCK_SIZE, add an additional padding block
             if (block_offset == clen)
             {
@@ -296,13 +301,13 @@ namespace Org.BouncyCastle.Crypto.Engines
             // Fill with ciphertext if available
             if (BLOCK_SIZE <= r_clen)
             { // enough ciphertext
-                Array.Copy(c, block_offset, output, 0, BLOCK_SIZE);
+                Array.Copy(c, cOff + block_offset, output, 0, BLOCK_SIZE);
             }
             else
             { // not enough ciphertext, need to pad
                 if (r_clen > 0) // c might be nullptr
                 {
-                    Array.Copy(c, block_offset, output, 0, r_clen);
+                    Array.Copy(c, cOff + block_offset, output, 0, r_clen);
                 }
                 Arrays.Fill(output, r_clen, BLOCK_SIZE, (byte)0);
                 output[r_clen] = 0x01;
@@ -313,10 +318,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 
         public void Init(bool forEncryption, ICipherParameters param)
         {
-            /**
-             * Elephant encryption and decryption is completely symmetrical, so the
-             * 'forEncryption' is irrelevant.
-             */
+            this.forEncryption = forEncryption;
             if (!(param is ParametersWithIV))
             {
                 throw new ArgumentException(
@@ -350,10 +352,14 @@ namespace Org.BouncyCastle.Crypto.Engines
             expanded_key = new byte[BLOCK_SIZE];
             Array.Copy(k, 0, expanded_key, 0, CRYPTO_KEYBYTES);
             permutation(expanded_key);
+            initialised = true;
+            reset(false);
         }
 
 
-        public string AlgorithmName => "Elephant AEAD";
+        public string AlgorithmName => algorithmName;
+
+        public IBlockCipher UnderlyingCipher => throw new NotImplementedException();
 
         public void ProcessAadByte(byte input)
         {
@@ -363,26 +369,48 @@ namespace Org.BouncyCastle.Crypto.Engines
 
         public void ProcessAadBytes(byte[] input, int inOff, int len)
         {
+            if (inOff + len > input.Length)
+            {
+                throw new DataLengthException("input buffer too short");
+            }
             aadData.Write(input, inOff, len);
         }
 
 
         public int ProcessByte(byte input, byte[] output, int outOff)
         {
-            return ProcessBytes(new byte[] { input }, 0, 1, output, outOff);
+            message.Write(new byte[] { input }, 0, 1);
+            return 0;
         }
 
 
         public int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
         {
-            if (encrypted)
+            if (inOff + len > input.Length)
+            {
+                throw new DataLengthException("input buffer too short");
+            }
+            message.Write(input, inOff, len);
+            return 0;
+        }
+
+
+        public int DoFinal(byte[] output, int outOff)
+        {
+            if (!initialised)
+            {
+                throw new ArgumentException("Need 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))
             {
-                throw new ArgumentException("Encryption has been processed");
+                throw new OutputLengthException("output buffer is too short");
             }
-            m = new byte[len];
-            Array.Copy(input, inOff, m, 0, len);
+            byte[] tag_buffer = new byte[BLOCK_SIZE];
+            byte[] m = message.GetBuffer();
             byte[] ad = aadData.GetBuffer();
-            int mlen = len, adlen = (int)aadData.Length;
+            int adlen = (int)aadData.Length;
             int nblocks_c = 1 + mlen / BLOCK_SIZE;
             int nblocks_m = (mlen % BLOCK_SIZE) != 0 ? nblocks_c : nblocks_c - 1;
             int nblocks_ad = 1 + (CRYPTO_NPUBBYTES + adlen) / BLOCK_SIZE;
@@ -415,11 +443,17 @@ namespace Org.BouncyCastle.Crypto.Engines
                     xor_block(buffer, m, offset, r_size);
                     Array.Copy(buffer, 0, output, offset + outOff, r_size);
                 }
-
                 if (i > 0 && i <= nblocks_c)
                 {
                     // Compute tag for ciphertext block
-                    get_c_block(buffer, output, outOff, mlen, i - 1);//encrypt != 0 ? c : m
+                    if (forEncryption)
+                    {
+                        get_c_block(buffer, output, outOff, mlen, i - 1);
+                    }
+                    else
+                    {
+                        get_c_block(buffer, m, 0, mlen, i - 1);
+                    }
                     xor_block(buffer, previous_mask, 0, BLOCK_SIZE);
                     xor_block(buffer, next_mask, 0, BLOCK_SIZE);
                     permutation(buffer);
@@ -444,30 +478,34 @@ namespace Org.BouncyCastle.Crypto.Engines
                 next_mask = temp;
                 offset += BLOCK_SIZE;
             }
-            encrypted = true;
-            return 0;
-        }
-
-
-        public int DoFinal(byte[] output, int outOff)
-        {
-            byte[] tag = GetMac();
-            Array.Copy(tag, 0, output, outOff, tag.Length);
+            outOff += mlen;
+            tag = new byte[CRYPTO_ABYTES];
+            xor_block(tag_buffer, expanded_key, 0, BLOCK_SIZE);
+            permutation(tag_buffer);
+            xor_block(tag_buffer, expanded_key, 0, BLOCK_SIZE);
+            if (forEncryption)
+            {
+                Array.Copy(tag_buffer, 0, tag, 0, CRYPTO_ABYTES);
+                Array.Copy(tag, 0, output, outOff, tag.Length);
+                mlen += CRYPTO_ABYTES;
+            }
+            else
+            {
+                for (int i = 0; i < CRYPTO_ABYTES; ++i)
+                {
+                    if (tag_buffer[i] != m[mlen + i])
+                    {
+                        throw new ArgumentException("Mac does not match");
+                    }
+                }
+            }
             reset(false);
-            return 0;
+            return mlen;
         }
 
 
         public byte[] GetMac()
         {
-            byte[] tag = new byte[CRYPTO_ABYTES];
-            if (!aadFinished)
-            {
-                xor_block(tag_buffer, expanded_key, 0, BLOCK_SIZE);
-                permutation(tag_buffer);
-                xor_block(tag_buffer, expanded_key, 0, BLOCK_SIZE);
-            }
-            Array.Copy(tag_buffer, 0, tag, 0, CRYPTO_ABYTES);
             return tag;
         }
 
@@ -493,11 +531,12 @@ namespace Org.BouncyCastle.Crypto.Engines
         {
             if (clearMac)
             {
-                aadData.SetLength(0);
+                tag = null;
             }
-            encrypted = false;
-            aadFinished = false;
+            aadData.SetLength(0);
+            message.SetLength(0);
         }
+
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         public void ProcessAadBytes(ReadOnlySpan<byte> input)
         {
@@ -506,28 +545,48 @@ namespace Org.BouncyCastle.Crypto.Engines
 
         public int ProcessByte(byte input, Span<byte> output)
         {
-            byte[] rv = new byte[1];
-            ProcessBytes(new byte[] { input }, 0, 1, rv, 0);
-            rv.AsSpan(0, 1).CopyTo(output);
-            return 1;
+            message.Write(new byte[] { input });
+            return 0;
         }
 
         public int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
         {
-            byte[] rv = new byte[input.Length];
-            ProcessBytes(input.ToArray(), 0, rv.Length, rv, 0);
-            rv.AsSpan(0, rv.Length).CopyTo(output);
-            return rv.Length;
+            message.Write(input.ToArray());
+            return 0;
         }
 
         public int DoFinal(Span<byte> output)
         {
-            byte[] tag = GetMac();
-            tag.AsSpan(0, tag.Length).CopyTo(output);
-            reset(false);
-            return tag.Length;
+            byte[] rv;
+            if (forEncryption)
+            {
+                rv = new byte[message.Length + CRYPTO_ABYTES];
+            }
+            else
+            {
+                rv = new byte[message.Length - CRYPTO_ABYTES];
+            }
+            int len = DoFinal(rv, 0);
+            rv.AsSpan(0, len).CopyTo(output);
+            return rv.Length;
+
         }
 #endif
+
+        public int GetKeyBytesSize()
+        {
+            return CRYPTO_KEYBYTES;
+        }
+
+        public int GetIVBytesSize()
+        {
+            return CRYPTO_NPUBBYTES;
+        }
+
+        public int GetBlockSize()
+        {
+            return BLOCK_SIZE;
+        }
     }
 }
 
diff --git a/crypto/test/src/crypto/test/ElephantTest.cs b/crypto/test/src/crypto/test/ElephantTest.cs
index ff350b338..8d12a20e0 100644
--- a/crypto/test/src/crypto/test/ElephantTest.cs
+++ b/crypto/test/src/crypto/test/ElephantTest.cs
@@ -8,7 +8,7 @@ using Org.BouncyCastle.Utilities.Test;
 using System.Collections.Generic;
 using System.IO;
 using Org.BouncyCastle.Crypto.Engines;
-
+using Org.BouncyCastle.Crypto.Modes;
 
 namespace BouncyCastle.Crypto.Tests
 {
@@ -25,6 +25,15 @@ namespace BouncyCastle.Crypto.Tests
             testVectors(ElephantEngine.ElephantParameters.elephant160, "v160");
             testVectors(ElephantEngine.ElephantParameters.elephant176, "v176");
             testVectors(ElephantEngine.ElephantParameters.elephant200, "v200");
+            ElephantEngine elephant = new ElephantEngine(ElephantEngine.ElephantParameters.elephant160);
+            testExceptions(elephant, elephant.GetKeyBytesSize(), elephant.GetIVBytesSize(), elephant.GetBlockSize());
+            testParameters(elephant, 16, 12, 8, 20);
+            elephant = new ElephantEngine(ElephantEngine.ElephantParameters.elephant176);
+            testExceptions(elephant, elephant.GetKeyBytesSize(), elephant.GetIVBytesSize(), elephant.GetBlockSize());
+            testParameters(elephant, 16, 12, 8, 22);
+            elephant = new ElephantEngine(ElephantEngine.ElephantParameters.elephant200);
+            testExceptions(elephant, elephant.GetKeyBytesSize(), elephant.GetIVBytesSize(), elephant.GetBlockSize());
+            testParameters(elephant, 16, 12, 16, 25);
         }
 
         private void testVectors(ElephantEngine.ElephantParameters pbp, String filename)
@@ -49,28 +58,43 @@ namespace BouncyCastle.Crypto.Tests
                         //{
                         //    continue;
                         //}
-                        param = new ParametersWithIV(new KeyParameter(Hex.Decode(map["Key"])), Hex.Decode(map["Nonce"]));
+                        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);
                         Elephant.Init(true, param);
-                        adByte = Hex.Decode(map["AD"]);
-                        Elephant.ProcessAadBytes(adByte, 0, adByte.Length);
-                        ptByte = Hex.Decode(map["PT"]);
-                        rv = new byte[Elephant.GetOutputSize(ptByte.Length)];
-                        Elephant.ProcessBytes(ptByte, 0, ptByte.Length, rv, 0);
-                        Elephant.DoFinal(rv, ptByte.Length);
-                        //foreach (byte b in Hex.Decode(map["CT"]))
+                        Elephant.ProcessAadBytes(ad, 0, ad.Length);
+                        rv = new byte[Elephant.GetOutputSize(pt.Length)];
+                        int len = Elephant.ProcessBytes(pt, 0, pt.Length, rv, 0);
+                        //byte[] mac = new byte[16];
+                        Elephant.DoFinal(rv, len);
+                        //foreach(byte b in Hex.Decode(map["CT"]))
                         //{
                         //    Console.Write(b.ToString("X2"));
                         //}
                         //Console.WriteLine();
-                        //foreach (byte b in rv)
+                        //foreach (byte b in Arrays.Concatenate(rv, mac))
                         //{
                         //    Console.Write(b.ToString("X2"));
                         //}
                         //Console.WriteLine();
-                        Assert.True(Arrays.AreEqual(rv, Hex.Decode(map["CT"])));
+                        Assert.True(Arrays.AreEqual(rv, ct));
+                        Elephant.Reset();
+                        Elephant.Init(false, param);
+                        //Decrypt
+                        Elephant.ProcessAadBytes(ad, 0, ad.Length);
+                        rv = new byte[pt.Length + 16];
+                        len = Elephant.ProcessBytes(ct, 0, ct.Length, rv, 0);
+                        Elephant.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();
                         Elephant.Reset();
+
                     }
                     else
                     {
@@ -87,6 +111,320 @@ namespace BouncyCastle.Crypto.Tests
                 }
             }
         }
+
+        private void testExceptions(IAeadBlockCipher aeadBlockCipher, int keysize, int ivsize, int blocksize)
+        {
+            ICipherParameters param;
+            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);
+            //try
+            //{
+            //    aeadBlockCipher.ProcessBytes(m, 0, m.Length, c1, 0);
+            //    Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialed before ProcessBytes");
+            //}
+            //catch (ArgumentException e)
+            //{
+            //    //expected
+            //}
+
+            //try
+            //{
+            //    aeadBlockCipher.ProcessByte((byte)0, c1, 0);
+            //    Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialed before ProcessByte");
+            //}
+            //catch (ArgumentException e)
+            //{
+            //    //expected
+            //}
+
+            //try
+            //{
+            //    aeadBlockCipher.Reset();
+            //    Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialed before reset");
+            //}
+            //catch (ArgumentException e)
+            //{
+            //    //expected
+            //}
+
+            try
+            {
+                aeadBlockCipher.DoFinal(c1, m.Length);
+                Assert.Fail(aeadBlockCipher.AlgorithmName + " need to be initialed before dofinal");
+            }
+            catch (ArgumentException e)
+            {
+                //expected
+            }
+
+            try
+            {
+                aeadBlockCipher.GetMac();
+                aeadBlockCipher.GetOutputSize(0);
+                aeadBlockCipher.GetUpdateOutputSize(0);
+            }
+            catch (ArgumentException e)
+            {
+                //expected
+                Assert.Fail(aeadBlockCipher.AlgorithmName + " functions can be called before initialisation");
+            }
+            Random rand = new Random();
+            int randomNum;
+            while ((randomNum = rand.Next(100)) == keysize) ;
+            byte[] k1 = new byte[randomNum];
+            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");
+            }
+            catch (ArgumentException e)
+            {
+                //expected
+            }
+            try
+            {
+                aeadBlockCipher.Init(true, new ParametersWithIV(new KeyParameter(k), iv1));
+                Assert.Fail(aeadBlockCipher.AlgorithmName + "iv size does not match");
+            }
+            catch (ArgumentException e)
+            {
+                //expected
+            }
+
+
+            aeadBlockCipher.Init(true, param);
+            try
+            {
+                aeadBlockCipher.DoFinal(c1, m.Length);
+            }
+            catch (Exception e)
+            {
+                Assert.Fail(aeadBlockCipher.AlgorithmName + " allows no input for AAD and plaintext");
+            }
+            byte[] mac2 = aeadBlockCipher.GetMac();
+            if (mac2 == null)
+            {
+                Assert.Fail("mac should not be empty after dofinal");
+            }
+            if (!Arrays.AreEqual(mac2, c1))
+            {
+                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);
+            if (Arrays.AreEqual(mac1, mac2))
+            {
+                Assert.Fail("mac should not match");
+            }
+            //aeadBlockCipher.Reset();
+            //aeadBlockCipher.ProcessBytes(new byte[blocksize], 0, blocksize, new byte[blocksize], 0);
+            //try
+            //{
+            //    aeadBlockCipher.ProcessAadByte((byte)0);
+            //    Assert.Fail("ProcessAadByte(s) cannot be called after encryption/decryption");
+            //}
+            //catch (ArgumentException e)
+            //{
+            //    //expected
+            //}
+            //try
+            //{
+            //    aeadBlockCipher.ProcessAadBytes(new byte[] { 0 }, 0, 1);
+            //    Assert.Fail("ProcessAadByte(s) cannot be called once only");
+            //}
+            //catch (ArgumentException e)
+            //{
+            //    //expected
+            //}
+
+            aeadBlockCipher.Reset();
+            try
+            {
+                aeadBlockCipher.ProcessAadBytes(new byte[] { 0 }, 1, 1);
+                Assert.Fail("input for ProcessAadBytes is too short");
+            }
+            catch (DataLengthException e)
+            {
+                //expected
+            }
+            try
+            {
+                aeadBlockCipher.ProcessBytes(new byte[] { 0 }, 1, 1, c1, 0);
+                Assert.Fail("input for ProcessBytes is too short");
+            }
+            catch (DataLengthException e)
+            {
+                //expected
+            }
+            //try
+            //{
+            //    aeadBlockCipher.ProcessBytes(new byte[blocksize], 0, blocksize, new byte[blocksize], blocksize >> 1);
+            //    Assert.Fail("output for ProcessBytes is too short");
+            //}
+            //catch (OutputLengthException e)
+            //{
+            //    //expected
+            //}
+            try
+            {
+                aeadBlockCipher.DoFinal(new byte[2], 2);
+                Assert.Fail("output for dofinal is too short");
+            }
+            catch (DataLengthException e)
+            {
+                //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);
+            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[] 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);
+            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);
+            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);
+            try
+            {
+                aeadBlockCipher.DoFinal(m4, offset);
+                Assert.Fail("The decryption should fail");
+            }
+            catch (ArgumentException e)
+            {
+                //expected;
+            }
+            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[] 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);
+            if (!Arrays.AreEqual(c7, c8) || !Arrays.AreEqual(c7, c9))
+            {
+                Assert.Fail("Splitting input of plaintext should output the same ciphertext");
+            }
+#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> c4_1 = new byte[c2.Length];
+            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);
+            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);
+            if (!Arrays.AreEqual(c2, c5))
+            {
+                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);
+            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);
+            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);
+            if (!Arrays.AreEqual(m2, m6))
+            {
+                Assert.Fail("mac should match for the same AAD and message with different offset for both input and output");
+            }
+#endif
+
+        }
+
+        private void testParameters(ElephantEngine Elephant, int keySize, int ivSize, int macSize, int blockSize)
+        {
+            if (Elephant.GetKeyBytesSize() != keySize)
+            {
+                Assert.Fail("key bytes of " + Elephant.AlgorithmName + " is not correct");
+            }
+            if (Elephant.GetIVBytesSize() != ivSize)
+            {
+                Assert.Fail("iv bytes of " + Elephant.AlgorithmName + " is not correct");
+            }
+            if (Elephant.GetOutputSize(0) != macSize)
+            {
+                Assert.Fail("mac bytes of " + Elephant.AlgorithmName + " is not correct");
+            }
+            if (Elephant.GetBlockSize() != blockSize)
+            {
+                Assert.Fail("block size of " + Elephant.AlgorithmName + " is not correct");
+            }
+            Console.WriteLine(Elephant.AlgorithmName + " test Parameters pass");
+        }
+
     }
 }