summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Hook <dgh@cryptoworkshop.com>2022-09-29 15:28:31 +1000
committerDavid Hook <dgh@cryptoworkshop.com>2022-09-29 15:28:31 +1000
commit12e4accafd737a5d3a8be6c554f9164a8c45ee15 (patch)
tree689058225c12aeed7fca17399317522f023f4d30
parentRemove unused field (diff)
downloadBouncyCastle.NET-ed25519-12e4accafd737a5d3a8be6c554f9164a8c45ee15.tar.xz
added grain128Aead
-rw-r--r--crypto/src/crypto/engines/Grain128AEADEngine.cs578
-rw-r--r--crypto/test/src/crypto/test/Grain128AeadTest.cs217
2 files changed, 795 insertions, 0 deletions
diff --git a/crypto/src/crypto/engines/Grain128AEADEngine.cs b/crypto/src/crypto/engines/Grain128AEADEngine.cs
new file mode 100644
index 000000000..e60368574
--- /dev/null
+++ b/crypto/src/crypto/engines/Grain128AEADEngine.cs
@@ -0,0 +1,578 @@
+using System;
+using System.IO;
+using System.Numerics;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+    public class Grain128AeadEngine//: AeadCipher
+    {
+
+        /**
+         * Constants
+         */
+        private static readonly int STATE_SIZE = 4;
+
+        /**
+         * Variables to hold the state of the engine during encryption and
+         * decryption
+         */
+        private byte[] workingKey;
+        private byte[] workingIV;
+        private uint[] lfsr;
+        private uint[] nfsr;
+        private uint[] authAcc;
+        private uint[] authSr;
+        private uint outputZ;
+
+        private bool initialised = false;
+        private bool isEven = true; // zero treated as even
+        private bool aadFinished = false;
+        private MemoryStream aadData = new MemoryStream();
+
+        private byte[] mac;
+
+
+        public String GetAlgorithmName()
+        {
+            return "Grain-128AEAD";
+        }
+
+        /**
+         * Initialize a Grain-128AEAD cipher.
+         *
+         * @param forEncryption Whether or not we are for encryption.
+         * @param param        The parameters required to set up the cipher.
+         * @throws ArgumentException If the params argument is inappropriate.
+         */
+        public void Init(bool forEncryption, ICipherParameters param)
+        {
+            /**
+             * Grain encryption and decryption is completely symmetrical, so the
+             * 'forEncryption' is irrelevant.
+             */
+            if (!(param is ParametersWithIV))
+            {
+                throw new ArgumentException(
+                    "Grain-128AEAD Init parameters must include an IV");
+            }
+
+            ParametersWithIV ivParams = (ParametersWithIV)param;
+
+            byte[]
+            iv = ivParams.GetIV();
+
+            if (iv == null || iv.Length != 12)
+            {
+                throw new ArgumentException(
+                    "Grain-128AEAD requires exactly 12 bytes of IV");
+            }
+
+            if (!(ivParams.Parameters is KeyParameter))
+            {
+                throw new ArgumentException(
+                    "Grain-128AEAD Init parameters must include a key");
+            }
+
+            KeyParameter key = (KeyParameter)ivParams.Parameters;
+            byte[] keyBytes = key.GetKey();
+            if (keyBytes.Length != 16)
+            {
+                throw new ArgumentException(
+                    "Grain-128AEAD key must be 128 bits long");
+            }
+            /**
+             * Initialize variables.
+             */
+            workingIV = new byte[key.GetKey().Length];
+            workingKey = new byte[key.GetKey().Length];
+            lfsr = new uint[STATE_SIZE];
+            nfsr = new uint[STATE_SIZE];
+            authAcc = new uint[2];
+            authSr = new uint[2];
+
+
+            Array.Copy(iv, 0, workingIV, 0, iv.Length);
+            Array.Copy(key.GetKey(), 0, workingKey, 0, key.GetKey().Length);
+
+            Reset();
+        }
+
+        /**
+         * 320 clocks initialization phase.
+         */
+        private void InitGrain()
+        {
+            for (int i = 0; i < 320; ++i)
+            {
+                outputZ = GetOutput();
+                nfsr = Shift(nfsr, (GetOutputNFSR() ^ lfsr[0] ^ outputZ) & 1);
+                lfsr = Shift(lfsr, (GetOutputLFSR() ^ outputZ) & 1);
+            }
+            for (int quotient = 0; quotient < 8; ++quotient)
+            {
+                for (int remainder = 0; remainder < 8; ++remainder)
+                {
+                    outputZ = GetOutput();
+                    nfsr = Shift(nfsr, (GetOutputNFSR() ^ lfsr[0] ^ outputZ ^ (uint)((workingKey[quotient]) >> remainder)) & 1);
+                    lfsr = Shift(lfsr, (GetOutputLFSR() ^ outputZ ^ (uint)((workingKey[quotient + 8]) >> remainder)) & 1);
+                }
+            }
+            for (int quotient = 0; quotient < 2; ++quotient)
+            {
+                for (int remainder = 0; remainder < 32; ++remainder)
+                {
+                    outputZ = GetOutput();
+                    nfsr = Shift(nfsr, (GetOutputNFSR() ^ lfsr[0]) & 1);
+                    lfsr = Shift(lfsr, (GetOutputLFSR()) & 1);
+                    authAcc[quotient] |= outputZ << remainder;
+                }
+            }
+            for (int quotient = 0; quotient < 2; ++quotient)
+            {
+                for (int remainder = 0; remainder < 32; ++remainder)
+                {
+                    outputZ = GetOutput();
+                    nfsr = Shift(nfsr, (GetOutputNFSR() ^ lfsr[0]) & 1);
+                    lfsr = Shift(lfsr, (GetOutputLFSR()) & 1);
+                    authSr[quotient] |= outputZ << remainder;
+                }
+            }
+            initialised = true;
+        }
+
+        /**
+         * Get output from non-linear function g(x).
+         *
+         * @return Output from NFSR.
+         */
+        private uint GetOutputNFSR()
+        {
+            uint b0 = nfsr[0];
+            uint b3 = nfsr[0] >> 3;
+            uint b11 = nfsr[0] >> 11;
+            uint b13 = nfsr[0] >> 13;
+            uint b17 = nfsr[0] >> 17;
+            uint b18 = nfsr[0] >> 18;
+            uint b22 = nfsr[0] >> 22;
+            uint b24 = nfsr[0] >> 24;
+            uint b25 = nfsr[0] >> 25;
+            uint b26 = nfsr[0] >> 26;
+            uint b27 = nfsr[0] >> 27;
+            uint b40 = nfsr[1] >> 8;
+            uint b48 = nfsr[1] >> 16;
+            uint b56 = nfsr[1] >> 24;
+            uint b59 = nfsr[1] >> 27;
+            uint b61 = nfsr[1] >> 29;
+            uint b65 = nfsr[2] >> 1;
+            uint b67 = nfsr[2] >> 3;
+            uint b68 = nfsr[2] >> 4;
+            uint b70 = nfsr[2] >> 6;
+            uint b78 = nfsr[2] >> 14;
+            uint b82 = nfsr[2] >> 18;
+            uint b84 = nfsr[2] >> 20;
+            uint b88 = nfsr[2] >> 24;
+            uint b91 = nfsr[2] >> 27;
+            uint b92 = nfsr[2] >> 28;
+            uint b93 = nfsr[2] >> 29;
+            uint b95 = nfsr[2] >> 31;
+            uint b96 = nfsr[3];
+
+            return (b0 ^ b26 ^ b56 ^ b91 ^ b96 ^ b3 & b67 ^ b11 & b13 ^ b17 & b18
+                ^ b27 & b59 ^ b40 & b48 ^ b61 & b65 ^ b68 & b84 ^ b22 & b24 & b25 ^ b70 & b78 & b82 ^ b88 & b92 & b93 & b95) & 1;
+        }
+
+        /**
+         * Get output from linear function f(x).
+         *
+         * @return Output from LFSR.
+         */
+        private uint GetOutputLFSR()
+        {
+            uint s0 = lfsr[0];
+            uint s7 = lfsr[0] >> 7;
+            uint s38 = lfsr[1] >> 6;
+            uint s70 = lfsr[2] >> 6;
+            uint s81 = lfsr[2] >> 17;
+            uint s96 = lfsr[3];
+
+            return (s0 ^ s7 ^ s38 ^ s70 ^ s81 ^ s96) & 1;
+        }
+
+        /**
+         * Get output from output function h(x).
+         *
+         * @return y_t.
+         */
+        private uint GetOutput()
+        {
+            uint b2 = nfsr[0] >> 2;
+            uint b12 = nfsr[0] >> 12;
+            uint b15 = nfsr[0] >> 15;
+            uint b36 = nfsr[1] >> 4;
+            uint b45 = nfsr[1] >> 13;
+            uint b64 = nfsr[2];
+            uint b73 = nfsr[2] >> 9;
+            uint b89 = nfsr[2] >> 25;
+            uint b95 = nfsr[2] >> 31;
+            uint s8 = lfsr[0] >> 8;
+            uint s13 = lfsr[0] >> 13;
+            uint s20 = lfsr[0] >> 20;
+            uint s42 = lfsr[1] >> 10;
+            uint s60 = lfsr[1] >> 28;
+            uint s79 = lfsr[2] >> 15;
+            uint s93 = lfsr[2] >> 29;
+            uint s94 = lfsr[2] >> 30;
+            return ((b12 & s8) ^ (s13 & s20) ^ (b95 & s42) ^ (s60 & s79) ^ (b12 & b95 & s94) ^ s93
+                ^ b2 ^ b15 ^ b36 ^ b45 ^ b64 ^ b73 ^ b89) & 1;
+        }
+
+        /**
+         * Shift array 1 bit and add val to index.Length - 1.
+         *
+         * @param array The array to shift.
+         * @param val   The value to shift in.
+         * @return The shifted array with val added to index.Length - 1.
+         */
+        private uint[] Shift(uint[] array, uint val)
+        {
+
+            array[0] = (array[0] >> 1) | (array[1] << 31);
+            array[1] = (array[1] >> 1) | (array[2] << 31);
+            array[2] = (array[2] >> 1) | (array[3] << 31);
+            array[3] = (array[3] >> 1) | (val << 31);
+
+            return array;
+        }
+
+        /**
+         * Set keys, reset cipher.
+         *
+         * @param keyBytes The key.
+         * @param ivBytes  The IV.
+         */
+        private void SetKey(byte[] keyBytes, byte[] ivBytes)
+        {
+            ivBytes[12] = (byte)0xFF;
+            ivBytes[13] = (byte)0xFF;
+            ivBytes[14] = (byte)0xFF;
+            ivBytes[15] = (byte)0x7F;//(byte) 0xFE;
+            workingKey = keyBytes;
+            workingIV = ivBytes;
+
+            /**
+             * Load NFSR and LFSR
+             */
+            int j = 0;
+            for (int i = 0; i < nfsr.Length; i++)
+            {
+                nfsr[i] = (uint)(((workingKey[j + 3]) << 24) | ((workingKey[j + 2]) << 16)
+                    & 0x00FF0000 | ((workingKey[j + 1]) << 8) & 0x0000FF00
+                    | ((workingKey[j]) & 0x000000FF));
+
+                lfsr[i] = (uint)(((workingIV[j + 3]) << 24) | ((workingIV[j + 2]) << 16)
+                    & 0x00FF0000 | ((workingIV[j + 1]) << 8) & 0x0000FF00
+                    | ((workingIV[j]) & 0x000000FF));
+                j += 4;
+            }
+        }
+
+        public int ProcessBytes(byte[] input, int inOff, int len, byte[] output,
+                                int outOff)
+        {
+            if (!initialised)
+            {
+                throw new ArgumentException(GetAlgorithmName()
+                    + " not initialised");
+            }
+            if (!aadFinished)
+            {
+                DoProcessAADBytes(aadData.GetBuffer(), 0, (int)aadData.Length);
+                aadFinished = true;
+            }
+
+
+            if ((inOff + len) > input.Length)
+            {
+                throw new DataLengthException("input buffer too short");
+            }
+
+            if ((outOff + len) > output.Length)
+            {
+                throw new OutputLengthException("output buffer too short");
+            }
+            GetKeyStream(input, inOff, len, output, outOff);
+            return len;
+        }
+
+        public void Reset()
+        {
+            this.isEven = true;
+            this.mac = null;
+            this.aadData.SetLength(0);
+            this.aadFinished = false;
+
+            SetKey(workingKey, workingIV);
+            InitGrain();
+        }
+
+        private byte[] GetKeyStream(byte[] input, int inOff, int len, byte[] ciphertext, int outOff)
+        {
+            int mCnt = 0, acCnt = 0, cCnt = 0;
+            byte cc;
+            byte[] plaintext = new byte[len];
+            for (int i = 0; i < len; ++i)
+            {
+                plaintext[i] = (byte)ReverseByte(input[inOff + i]);
+            }
+            for (int i = 0; i < len; ++i)
+            {
+                cc = 0;
+                for (int j = 0; j < 16; ++j)
+                {
+                    outputZ = GetOutput();
+                    nfsr = Shift(nfsr, (GetOutputNFSR() ^ lfsr[0]) & 1);
+                    lfsr = Shift(lfsr, (GetOutputLFSR()) & 1);
+                    if (isEven)
+                    {
+                        cc |= (byte)(((((plaintext[mCnt >> 3]) >> (7 - (mCnt & 7))) & 1) ^ outputZ) << (cCnt & 7));
+                        mCnt++;
+                        cCnt++;
+                        isEven = false;
+                    }
+                    else
+                    {
+
+                        if ((plaintext[acCnt >> 3] & (1 << (7 - (acCnt & 7)))) != 0)
+                        {
+                            Accumulate();
+                        }
+                        AuthShift(outputZ);
+                        acCnt++;
+                        isEven = true;
+                    }
+                }
+                ciphertext[outOff + i] = cc;
+            }
+            //outputZ = GetOutput();
+            //nfsr = Shift(nfsr, (GetOutputNFSR() ^ lfsr[0]) & 1);
+            //lfsr = Shift(lfsr, (GetOutputLFSR()) & 1);
+            //Accumulate();
+            //cCnt = len + outOff;//acc_idx
+            //for (int i = 0; i < 2; ++i)
+            //{
+            //    for (int j = 0; j < 4; ++j)
+            //    {
+            //        ciphertext[cCnt] = (byte)((authAcc[i] >> (j << 3)) & 0xff);
+            //        cCnt++;
+            //    }
+            //}
+
+            return ciphertext;
+        }
+
+
+        public byte ReturnByte(byte input)
+        {
+            if (!initialised)
+            {
+                throw new ArgumentException(GetAlgorithmName()
+                    + " not initialised");
+            }
+            byte[] plaintext = new byte[1];
+            plaintext[0] = input;
+            byte[] ciphertext = new byte[1];
+            return GetKeyStream(plaintext, 0, 1, ciphertext, 0)[0];
+        }
+
+
+        public void ProcessAADByte(byte input)
+        {
+            if (aadFinished)
+            {
+                throw new ArgumentException("associated data must be added before plaintext/ciphertext");
+            }
+            aadData.Write(new byte[] { input }, 0, 1);
+
+        }
+
+        public void ProcessAADBytes(byte[] input, int inOff, int len)
+        {
+            if (aadFinished)
+            {
+                throw new ArgumentException("associated data must be added before plaintext/ciphertext");
+            }
+            aadData.Write(input, inOff, len);
+        }
+
+        private void Accumulate()
+        {
+            authAcc[0] ^= authSr[0];
+            authAcc[1] ^= authSr[1];
+        }
+
+        private void AuthShift(uint val)
+        {
+            authSr[0] = (authSr[0] >> 1) | (authSr[1] << 31);
+            authSr[1] = (authSr[1] >> 1) | (val << 31);
+        }
+
+
+        public int ProcessByte(byte input, byte[] output, int outOff)
+        {
+            return ProcessBytes(new byte[] { input }, 0, 1, output, outOff);
+        }
+
+        private void DoProcessAADBytes(byte[] input, int inOff, int len)
+        {
+            byte[] ader;
+            int aderlen;
+            //encodeDer
+            if (len < 128)
+            {
+                ader = new byte[1 + len];
+                ader[0] = (byte)ReverseByte((uint)len);
+                aderlen = 0;
+            }
+            else
+            {
+                aderlen = LenLength(len);
+                ader = new byte[aderlen + 1 + len];
+                ader[0] = (byte)ReverseByte(0x80 | (uint)aderlen);
+                uint tmp = (uint)len;
+                for (int i = 0; i < aderlen; ++i)
+                {
+                    ader[1 + i] = (byte)ReverseByte(tmp & 0xff);
+                    tmp >>= 8;
+                }
+            }
+            for (int i = 0; i < len; ++i)
+            {
+                ader[1 + aderlen + i] = (byte)ReverseByte(input[inOff + i]);
+            }
+            byte adval;
+            int adCnt = 0;
+            for (int i = 0; i < ader.Length; ++i)
+            {
+                for (int j = 0; j < 16; ++j)
+                {
+                    outputZ = GetOutput();
+                    nfsr = Shift(nfsr, (GetOutputNFSR() ^ lfsr[0]) & 1);
+                    lfsr = Shift(lfsr, (GetOutputLFSR()) & 1);
+                    if ((j & 1) == 1)
+                    {
+                        adval = (byte)(ader[adCnt >> 3] & (1 << (7 - (adCnt & 7))));
+                        if (adval != 0)
+                        {
+                            Accumulate();
+                        }
+                        AuthShift(outputZ);
+                        adCnt++;
+                    }
+                }
+            }
+
+
+        }
+
+        private int LenLength(int v)
+        {
+            if ((v & 0xff) == v)
+            {
+                return 1;
+            }
+            if ((v & 0xffff) == v)
+            {
+                return 2;
+            }
+            if ((v & 0xffffff) == v)
+            {
+                return 3;
+            }
+
+            return 4;
+        }
+
+        public int DoFinal(byte[] output, int outOff)
+        {
+            if (!aadFinished)
+            {
+                DoProcessAADBytes(aadData.GetBuffer(), 0, (int)aadData.Length);
+                aadFinished = true;
+            }
+
+            this.mac = new byte[8];
+
+            outputZ = GetOutput();
+            nfsr = Shift(nfsr, (GetOutputNFSR() ^ lfsr[0]) & 1);
+            lfsr = Shift(lfsr, (GetOutputLFSR()) & 1);
+            Accumulate();
+
+            int cCnt = 0;
+            for (int i = 0; i < 2; ++i)
+            {
+                for (int j = 0; j < 4; ++j)
+                {
+                    mac[cCnt++] = (byte)((authAcc[i] >> (j << 3)) & 0xff);
+                }
+            }
+
+            Array.Copy(mac, 0, output, outOff, mac.Length);
+
+            try
+            {
+                return mac.Length;
+            }
+            finally
+            {
+                Reset();
+            }
+
+        }
+
+
+        public byte[] GetMac()
+        {
+            return mac;
+        }
+
+
+        public int GetUpdateOutputSize(int len)
+        {
+            return len;
+        }
+
+
+        public int GetOutputSize(int len)
+        {
+            return len + 8;
+        }
+
+        private uint ReverseByte(uint x)
+        {
+            x = (uint)(((x & 0x55) << 1) | ((x & (~0x55)) >> 1)) & 0xFF;
+            x = (uint)(((x & 0x33) << 2) | ((x & (~0x33)) >> 2)) & 0xFF;
+            x = (uint)(((x & 0x0f) << 4) | ((x & (~0x0f)) >> 4)) & 0xFF;
+            return x;
+        }
+
+        public uint HighestOneBit(uint v)
+        {
+            int[] MultiplyDeBruijnBitPosition ={
+      0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
+      8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
+   };
+            v |= v >> 1;
+            v |= v >> 2;
+            v |= v >> 4;
+            v |= v >> 8;
+            v |= v >> 16;
+
+            return (uint)(1 << MultiplyDeBruijnBitPosition[(v * 0x07C4ACDDU) >> 27]);
+        }
+    }
+}
+
diff --git a/crypto/test/src/crypto/test/Grain128AeadTest.cs b/crypto/test/src/crypto/test/Grain128AeadTest.cs
new file mode 100644
index 000000000..049df3354
--- /dev/null
+++ b/crypto/test/src/crypto/test/Grain128AeadTest.cs
@@ -0,0 +1,217 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using NUnit.Framework;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+    [TestFixture]
+    public class Grain128AeadTest : SimpleTest
+    {
+        public override string Name
+        {
+            get { return "Grain-128Aead"; }
+        }
+
+        [Test]
+        public override void PerformTest()
+        {
+            testVectors();
+            testSplitUpdate();
+            testLongAead();
+            testExceptions();
+        }
+
+
+        private void testVectors()
+        {
+            Grain128AeadEngine grain = new Grain128AeadEngine();
+            ICipherParameters param;
+            var buf = new Dictionary<string, string>();
+            //TestSampler sampler = new TestSampler();
+            using (var src = new StreamReader(SimpleTest.GetTestDataAsStream("crypto.LWC_Aead_KAT_128_96.txt")))
+            {
+                string line;
+                string[] data;
+                byte[] ptByte, adByte;
+                byte[] rv;
+                Dictionary<string, string> map = new Dictionary<string, string>();
+                while ((line = src.ReadLine()) != null)
+                {
+                    data = line.Split(' ');
+                    if (data.Length == 1)
+                    {
+                        param = new ParametersWithIV(new KeyParameter(Hex.Decode(map["Key"])), Hex.Decode(map["Nonce"]));
+                        grain.Init(true, param);
+                        adByte = Hex.Decode(map["AD"]);
+                        grain.ProcessAADBytes(adByte, 0, adByte.Length);
+                        ptByte = Hex.Decode(map["PT"]);
+                        rv = new byte[ptByte.Length];
+                        grain.ProcessBytes(ptByte, 0, ptByte.Length, rv, 0);
+                        byte[] mac = new byte[8];
+                        grain.DoFinal(mac, 0);
+                        Assert.True(Arrays.AreEqual(Arrays.Concatenate(rv, mac), Hex.Decode(map["CT"])));
+                        map.Clear();
+                    }
+                    else
+                    {
+                        if (data.Length >= 3)
+                        {
+                            map[data[0].Trim()] = data[2].Trim();
+                        }
+                        else
+                        {
+                            map[data[0].Trim()] = "";
+                        }
+
+                    }
+                }
+            }
+        }
+
+        private void testSplitUpdate()
+        {
+            byte[] Key = Hex.Decode("000102030405060708090A0B0C0D0E0F");
+            byte[] Nonce = Hex.Decode("000102030405060708090A0B");
+            byte[] PT = Hex.Decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+            byte[] AD = Hex.Decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E");
+            byte[] CT = Hex.Decode("EAD60EF559493ACEF6A3C238C018835DE3ABB6AA621A9AA65EFAF7B9D05BBE6C0913DFC8674BACC9");
+
+            Grain128AeadEngine grain = new Grain128AeadEngine();
+            ParametersWithIV param = new ParametersWithIV(new KeyParameter(Key), Nonce);
+            grain.Init(true, param);
+
+            grain.ProcessAADBytes(AD, 0, 10);
+            grain.ProcessAADByte(AD[10]);
+            grain.ProcessAADBytes(AD, 11, AD.Length - 11);
+
+            byte[] rv = new byte[CT.Length];
+            int len = grain.ProcessBytes(PT, 0, 10, rv, 0);
+            len += grain.ProcessByte(PT[10], rv, len);
+            len += grain.ProcessBytes(PT, 11, PT.Length - 11, rv, len);
+
+            grain.DoFinal(rv, len);
+
+            Assert.True(Arrays.AreEqual(rv, CT));
+
+            grain.ProcessBytes(PT, 0, 10, rv, 0);
+            try
+            {
+                grain.ProcessAADByte((byte)0x01);
+                Assert.Fail("no exception");
+            }
+            catch (ArgumentException e)
+            {
+                Assert.IsTrue(Contains(e.Message, "associated data must be added before plaintext/ciphertext"));
+            }
+
+            try
+            {
+                grain.ProcessAADBytes(AD, 0, AD.Length);
+                Assert.Fail("no exception");
+            }
+            catch (ArgumentException e)
+            {
+                Assert.IsTrue(Contains(e.Message, "associated data must be added before plaintext/ciphertext"));
+            }
+        }
+
+        private bool Contains(string message, string sub)
+        {
+            return message.IndexOf(sub) >= 0;
+        }
+
+        private void testLongAead()
+        {
+            byte[] Key = Hex.Decode("000102030405060708090A0B0C0D0E0F");
+            byte[] Nonce = Hex.Decode("000102030405060708090A0B");
+            byte[] PT = Hex.Decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+            byte[] AD = Hex.Decode(   // 186 bytes
+                "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9");
+            byte[] CT = Hex.Decode("731DAA8B1D15317A1CCB4E3DD320095FB27E5BB2A10F2C669F870538637D4F162298C70430A2B560");
+
+            Grain128AeadEngine grain = new Grain128AeadEngine();
+            ParametersWithIV param = new ParametersWithIV(new KeyParameter(Key), Nonce);
+            grain.Init(true, param);
+
+            grain.ProcessAADBytes(AD, 0, AD.Length);
+
+            byte[] rv = new byte[CT.Length];
+            int len = grain.ProcessBytes(PT, 0, 10, rv, 0);
+            len += grain.ProcessByte(PT[10], rv, len);
+            len += grain.ProcessBytes(PT, 11, PT.Length - 11, rv, len);
+
+            grain.DoFinal(rv, len);
+
+            Assert.IsTrue(Arrays.AreEqual(rv, CT));
+
+            grain.ProcessBytes(PT, 0, 10, rv, 0);
+            try
+            {
+                grain.ProcessAADByte((byte)0x01);
+                Assert.Fail("no exception");
+            }
+            catch (ArgumentException e)
+            {
+                Assert.IsTrue(Contains(e.Message, "associated data must be added before plaintext/ciphertext"));
+            }
+
+            try
+            {
+                grain.ProcessAADBytes(AD, 0, AD.Length);
+                Assert.Fail("no exception");
+            }
+            catch (ArgumentException e)
+            {
+                Assert.IsTrue(Contains(e.Message, "associated data must be added before plaintext/ciphertext"));
+            }
+        }
+
+        private void testExceptions()
+
+        {
+            try
+            {
+                Grain128AeadEngine grain128 = new Grain128AeadEngine();
+
+                grain128.Init(true, new KeyParameter(new byte[10]));
+                Assert.Fail("no exception");
+            }
+            catch (ArgumentException e)
+            {
+                Assert.IsTrue(Contains(e.Message, "Grain-128Aead Init parameters must include an IV"));
+            }
+
+            try
+            {
+                Grain128AeadEngine grain128 = new Grain128AeadEngine();
+
+                grain128.Init(true, new ParametersWithIV(new KeyParameter(new byte[10]), new byte[8]));
+                Assert.Fail("no exception");
+            }
+            catch (ArgumentException e)
+            {
+                Assert.IsTrue(Contains(e.Message, "Grain-128Aead requires exactly 12 bytes of IV"));
+            }
+
+            try
+            {
+                Grain128AeadEngine grain128 = new Grain128AeadEngine();
+
+                grain128.Init(true, new ParametersWithIV(new KeyParameter(new byte[10]), new byte[12]));
+                Assert.Fail("no exception");
+            }
+            catch (ArgumentException e)
+            {
+                Assert.IsTrue(Contains(e.Message, "Grain-128Aead key must be 128 bits long"));
+            }
+        }
+
+    }
+}
+