summary refs log tree commit diff
path: root/crypto/src
diff options
context:
space:
mode:
authorgefeili <gli@keyfactor.com>2023-01-25 11:47:18 +1030
committergefeili <gli@keyfactor.com>2023-01-25 11:47:18 +1030
commit876ff0e8bacc3bbed2345e25aea8d12b0a75d601 (patch)
tree0cf2ac5808cff531240342e4bc5166e90cb04be5 /crypto/src
parentFix method name (obsolete old one) (diff)
downloadBouncyCastle.NET-ed25519-876ff0e8bacc3bbed2345e25aea8d12b0a75d601.tar.xz
Add ISAP to the master branch
Diffstat (limited to 'crypto/src')
-rw-r--r--crypto/src/crypto/digests/ISAPDigest.cs149
-rw-r--r--crypto/src/crypto/engines/ISAPEngine.cs1036
2 files changed, 1185 insertions, 0 deletions
diff --git a/crypto/src/crypto/digests/ISAPDigest.cs b/crypto/src/crypto/digests/ISAPDigest.cs
new file mode 100644
index 000000000..3be28e4e2
--- /dev/null
+++ b/crypto/src/crypto/digests/ISAPDigest.cs
@@ -0,0 +1,149 @@
+using System;
+using System.IO;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+    public class ISAPDigest : IDigest
+    {
+        private ulong x0, x1, x2, x3, x4;
+        private ulong t0, t1, t2, t3, t4;
+        private MemoryStream buffer = new MemoryStream();
+
+        private void ROUND(ulong C)
+        {
+            t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C));
+            t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3));
+            t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4);
+            t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4));
+            t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1);
+            x0 = t0 ^ ROTR(t0, 19) ^ ROTR(t0, 28);
+            x1 = t1 ^ ROTR(t1, 39) ^ ROTR(t1, 61);
+            x2 = ~(t2 ^ ROTR(t2, 1) ^ ROTR(t2, 6));
+            x3 = t3 ^ ROTR(t3, 10) ^ ROTR(t3, 17);
+            x4 = t4 ^ ROTR(t4, 7) ^ ROTR(t4, 41);
+        }
+
+        private void P12()
+        {
+            ROUND(0xf0);
+            ROUND(0xe1);
+            ROUND(0xd2);
+            ROUND(0xc3);
+            ROUND(0xb4);
+            ROUND(0xa5);
+            ROUND(0x96);
+            ROUND(0x87);
+            ROUND(0x78);
+            ROUND(0x69);
+            ROUND(0x5a);
+            ROUND(0x4b);
+        }
+
+        private ulong ROTR(ulong x, int n)
+        {
+            return (x >> n) | (x << (64 - n));
+        }
+
+        protected ulong U64BIG(ulong x)
+        {
+            return ((ROTR(x, 8) & (0xFF000000FF000000UL)) | (ROTR(x, 24) & (0x00FF000000FF0000UL)) |
+                (ROTR(x, 40) & (0x0000FF000000FF00UL)) | (ROTR(x, 56) & (0x000000FF000000FFUL)));
+        }
+
+        public string AlgorithmName
+        {
+            get { return "ISAP Hash"; }
+        }
+
+        public void BlockUpdate(byte[] input, int inOff, int inLen)
+        {
+            if (inOff + inLen > input.Length)
+            {
+                throw new DataLengthException("input buffer too short");
+            }
+            buffer.Write(input, inOff, inLen);
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            buffer.Write(input.ToArray(), 0, input.Length);
+        }
+
+        public int DoFinal(Span<byte> output)
+        {
+            byte[] rv = new byte[32];
+            int rlt = DoFinal(rv, 0);
+            rv.AsSpan(0, 32).CopyTo(output);
+            return rlt;
+        }
+
+#endif
+
+        public int DoFinal(byte[] output, int outOff)
+        {
+            if (32 + outOff > output.Length)
+            {
+                throw new OutputLengthException("output buffer is too short");
+            }
+            t0 = t1 = t2 = t3 = t4 = 0;
+            /* init state */
+            x0 = 17191252062196199485UL;
+            x1 = 10066134719181819906UL;
+            x2 = 13009371945472744034UL;
+            x3 = 4834782570098516968UL;
+            x4 = 3787428097924915520UL;
+            /* absorb */
+            byte[] input = buffer.GetBuffer();
+            int len = (int)buffer.Length;
+            ulong[] in64 = new ulong[len >> 3];
+            Pack.LE_To_UInt64(input, 0, in64, 0, in64.Length);
+            int idx = 0;
+            while (len >= 8)
+            {
+                x0 ^= U64BIG(in64[idx++]);
+                P12();
+                len -= 8;
+            }
+            /* absorb final input block */
+            x0 ^= 0x80UL << ((7 - len) << 3);
+            while (len > 0)
+            {
+                x0 ^= (input[(idx << 3) + --len] & 0xFFUL) << ((7 - len) << 3);
+            }
+            P12();
+            // squeeze
+            ulong[] out64 = new ulong[4];
+            for (idx = 0; idx < 3; ++idx)
+            {
+                out64[idx] = U64BIG(x0);
+                P12();
+            }
+            /* squeeze final output block */
+            out64[idx] = U64BIG(x0);
+            Pack.UInt64_To_LE(out64, output, outOff);
+            return 32;
+        }
+
+        public int GetByteLength()
+        {
+            throw new NotImplementedException();
+        }
+
+        public int GetDigestSize()
+        {
+            return 32;
+        }
+
+        public void Reset()
+        {
+            buffer.SetLength(0);
+        }
+
+        public void Update(byte input)
+        {
+            buffer.Write(new byte[] { input }, 0, 1);
+        }
+    }
+}
diff --git a/crypto/src/crypto/engines/ISAPEngine.cs b/crypto/src/crypto/engines/ISAPEngine.cs
new file mode 100644
index 000000000..3745f3ae9
--- /dev/null
+++ b/crypto/src/crypto/engines/ISAPEngine.cs
@@ -0,0 +1,1036 @@
+using System;
+using System.IO;
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
+using static Org.BouncyCastle.Tls.DtlsReliableHandshake;
+
+
+/**
+ * ISAP AEAD v2, https://isap.iaik.tugraz.at/
+ * https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/constist-round/updated-spec-doc/isap-spec-const.pdf
+ * <p>
+ * ISAP AEAD v2 with reference to C Reference Impl from: https://github.com/isap-lwc/isap-code-package
+ * </p>
+ */
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+    public class ISAPEngine : IAeadBlockCipher
+    {
+        public enum IsapType
+        {
+            ISAP_A_128A,
+            ISAP_K_128A,
+            ISAP_A_128,
+            ISAP_K_128
+        }
+
+        public ISAPEngine(IsapType isapType)
+        {
+            switch (isapType)
+            {
+                case IsapType.ISAP_A_128A:
+                    ISAPAEAD = new ISAPAEAD_A_128A();
+                    ISAP_rH = 64;
+                    algorithmName = "ISAP-A-128A AEAD";
+                    break;
+                case IsapType.ISAP_K_128A:
+                    ISAPAEAD = new ISAPAEAD_K_128A();
+                    ISAP_rH = 144;
+                    algorithmName = "ISAP-K-128A AEAD";
+                    break;
+                case IsapType.ISAP_A_128:
+                    ISAPAEAD = new ISAPAEAD_A_128();
+                    ISAP_rH = 64;
+                    algorithmName = "ISAP-A-128 AEAD";
+                    break;
+                case IsapType.ISAP_K_128:
+                    ISAPAEAD = new ISAPAEAD_K_128();
+                    ISAP_rH = 144;
+                    algorithmName = "ISAP-K-128 AEAD";
+                    break;
+            }
+            ISAP_rH_SZ = (ISAP_rH + 7) >> 3;
+        }
+
+        private string algorithmName;
+        private bool forEncryption;
+        private bool initialised;
+        const int CRYPTO_KEYBYTES = 16;
+        const int CRYPTO_NPUBBYTES = 16;
+        const int ISAP_STATE_SZ = 40;
+        private byte[] c;
+        private byte[] ad;
+        private byte[] mac;
+        private MemoryStream aadData = new MemoryStream();
+        private MemoryStream message = new MemoryStream();
+        private MemoryStream outputStream = new MemoryStream();
+        private ISAP_AEAD ISAPAEAD;
+        private int ISAP_rH;
+        private int ISAP_rH_SZ;
+
+        public IBlockCipher UnderlyingCipher => throw new NotImplementedException();
+
+        public string AlgorithmName => algorithmName;
+
+        protected abstract class ISAP_AEAD
+        {
+            protected byte[] k;
+            protected byte[] npub;
+            protected int ISAP_rH;
+            protected int ISAP_rH_SZ;
+
+            public abstract void isap_enc(byte[] m, int mOff, int mlen, byte[] c, int cOff, int clen);
+
+            public abstract void init(byte[] k, byte[] npub, int ISAP_rH, int ISAP_rH_SZ);
+
+            public abstract void isap_mac(byte[] ad, int adlen, byte[] c, int clen, byte[] tag, int tagOff);
+
+            public abstract void reset();
+        }
+
+        protected abstract class ISAPAEAD_A : ISAP_AEAD
+        {
+            protected ulong[] k64;
+            protected ulong[] npub64;
+            protected ulong ISAP_IV1_64;
+            protected ulong ISAP_IV2_64;
+            protected ulong ISAP_IV3_64;
+            protected ulong x0, x1, x2, x3, x4, t0, t1, t2, t3, t4;
+
+            public override void init(byte[] k, byte[] npub, int ISAP_rH, int ISAP_rH_SZ)
+            {
+                this.k = k;
+                this.npub = npub;
+                this.ISAP_rH = ISAP_rH;
+                this.ISAP_rH_SZ = ISAP_rH_SZ;
+                npub64 = new ulong[getulongSize(npub.Length)];
+                Pack.LE_To_UInt64(npub, 0, npub64, 0, npub64.Length);
+                npub64[0] = U64BIG(npub64[0]);
+                npub64[1] = U64BIG(npub64[1]);
+                k64 = new ulong[getulongSize(k.Length)];
+                Pack.LE_To_UInt64(k, 0, k64, 0, k64.Length);
+                k64[0] = U64BIG(k64[0]);
+                k64[1] = U64BIG(k64[1]);
+                reset();
+            }
+
+            protected abstract void PX1();
+
+            protected abstract void PX2();
+
+            protected void ABSORB_MAC(byte[] src, int len)
+            {
+                ulong[] src64 = new ulong[src.Length >> 3];
+                Pack.LE_To_UInt64(src, 0, src64, 0, src64.Length);
+                int idx = 0;
+                while (len >= ISAP_rH_SZ)
+                {
+                    x0 ^= U64BIG(src64[idx++]);
+                    P12();
+                    len -= ISAP_rH_SZ;
+                }
+                /* Absorb const ad block */
+                for (int i = 0; i < len; ++i)
+                {
+                    x0 ^= (src[(idx << 3) + i] & 0xFFUL) << ((7 - i) << 3);
+                }
+                x0 ^= 0x80UL << ((7 - len) << 3);
+                P12();
+            }
+
+            public override void isap_mac(byte[] ad, int adlen, byte[] c, int clen, byte[] tag, int tagOff)
+            {
+                // Init State
+                x0 = npub64[0];
+                x1 = npub64[1];
+                x2 = ISAP_IV1_64;
+                x3 = x4 = 0;
+                P12();
+                ABSORB_MAC(ad, adlen);
+                // Domain seperation
+                x4 ^= 1L;
+                ABSORB_MAC(c, clen);
+                // Derive K*
+                Pack.UInt64_To_LE(U64BIG(x0), tag, 0);
+                Pack.UInt64_To_LE(U64BIG(x1), tag, 8);
+                ulong tmp_x2 = x2, tmp_x3 = x3, tmp_x4 = x4;
+                isap_rk(ISAP_IV2_64, tag, CRYPTO_KEYBYTES);
+                x2 = tmp_x2;
+                x3 = tmp_x3;
+                x4 = tmp_x4;
+                // Squeeze tag
+                P12();
+                Pack.UInt64_To_LE(U64BIG(x0), tag, tagOff);
+                Pack.UInt64_To_LE(U64BIG(x1), tag, tagOff + 8);
+            }
+
+            public void isap_rk(ulong iv64, byte[] y, int ylen)
+            {
+                // Init state
+                x0 = k64[0];
+                x1 = k64[1];
+                x2 = iv64;
+                x3 = x4 = 0;
+                P12();
+                // Absorb Y
+                for (int i = 0; i < (ylen << 3) - 1; i++)
+                {
+                    x0 ^= (((((ulong)y[i >> 3] >> (7 - (i & 7))) & 0x01UL) << 7) & 0xFFUL) << 56;
+                    PX2();
+                }
+                x0 ^= (((y[ylen - 1]) & 0x01UL) << 7) << 56;
+                P12();
+            }
+
+            public override void isap_enc(byte[] m, int mOff, int mlen, byte[] c, int cOff, int clen)
+            {
+                /* Encrypt m */
+                ulong[] m64 = new ulong[mlen >> 3];
+                Pack.LE_To_UInt64(m, mOff, m64, 0, m64.Length);
+                ulong[] c64 = new ulong[m64.Length];
+                int idx = 0;
+                while (mlen >= ISAP_rH_SZ)
+                {
+                    c64[idx] = U64BIG(x0) ^ m64[idx];
+                    PX1();
+                    idx++;
+                    mlen -= ISAP_rH_SZ;
+                }
+                Pack.UInt64_To_LE(c64, 0, c64.Length, c, cOff);
+                /* Encrypt const m block */
+                byte[] xo = Pack.UInt64_To_LE(x0);
+                while (mlen > 0)
+                {
+                    c[(idx << 3) + cOff + mlen - 1] = (byte)(xo[ISAP_rH_SZ - mlen] ^ m[(idx << 3) + mOff + --mlen]);
+                }
+            }
+
+            public override void reset()
+            {
+                // Init state
+                isap_rk(ISAP_IV3_64, npub, CRYPTO_NPUBBYTES);
+                x3 = npub64[0];
+                x4 = npub64[1];
+                PX1();
+            }
+
+            private int getulongSize(int x)
+            {
+                return (x >> 3) + ((x & 7) != 0 ? 1 : 0);
+            }
+
+            private ulong ROTR(ulong x, int n)
+            {
+                return (x >> n) | (x << (64 - n));
+            }
+
+            protected ulong U64BIG(ulong x)
+            {
+                return ((ROTR(x, 8) & (0xFF000000FF000000UL)) | (ROTR(x, 24) & (0x00FF000000FF0000UL)) |
+                    (ROTR(x, 40) & (0x0000FF000000FF00UL)) | (ROTR(x, 56) & (0x000000FF000000FFUL)));
+            }
+
+            protected void ROUND(ulong C)
+            {
+                t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C));
+                t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3));
+                t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4);
+                t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4));
+                t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1);
+                x0 = t0 ^ ROTR(t0, 19) ^ ROTR(t0, 28);
+                x1 = t1 ^ ROTR(t1, 39) ^ ROTR(t1, 61);
+                x2 = ~(t2 ^ ROTR(t2, 1) ^ ROTR(t2, 6));
+                x3 = t3 ^ ROTR(t3, 10) ^ ROTR(t3, 17);
+                x4 = t4 ^ ROTR(t4, 7) ^ ROTR(t4, 41);
+            }
+
+            public void P12()
+            {
+                ROUND(0xf0);
+                ROUND(0xe1);
+                ROUND(0xd2);
+                ROUND(0xc3);
+                ROUND(0xb4);
+                ROUND(0xa5);
+                P6();
+            }
+
+            protected void P6()
+            {
+                ROUND(0x96);
+                ROUND(0x87);
+                ROUND(0x78);
+                ROUND(0x69);
+                ROUND(0x5a);
+                ROUND(0x4b);
+            }
+        }
+
+        private class ISAPAEAD_A_128A : ISAPAEAD_A
+        {
+            public ISAPAEAD_A_128A()
+            {
+                ISAP_IV1_64 = 108156764297430540UL;
+                ISAP_IV2_64 = 180214358335358476UL;
+                ISAP_IV3_64 = 252271952373286412UL;
+            }
+
+            protected override void PX1()
+            {
+                P6();
+            }
+
+            protected override void PX2()
+            {
+                ROUND(0x4b);
+            }
+        }
+
+        private class ISAPAEAD_A_128 : ISAPAEAD_A
+        {
+            public ISAPAEAD_A_128()
+            {
+                ISAP_IV1_64 = 108156764298152972L;
+                ISAP_IV2_64 = 180214358336080908L;
+                ISAP_IV3_64 = 252271952374008844L;
+            }
+
+            protected override void PX1()
+            {
+                P12();
+            }
+
+            protected override void PX2()
+            {
+                P12();
+            }
+        }
+
+        private abstract class ISAPAEAD_K : ISAP_AEAD
+        {
+            const int ISAP_STATE_SZ_CRYPTO_NPUBBYTES = ISAP_STATE_SZ - CRYPTO_NPUBBYTES;
+            protected ushort[] ISAP_IV1_16;
+            protected ushort[] ISAP_IV2_16;
+            protected ushort[] ISAP_IV3_16;
+            protected ushort[] k16;
+            protected ushort[] iv16;
+            private readonly int[] KeccakF400RoundConstants = {0x0001, 0x8082, 0x808a, 0x8000, 0x808b, 0x0001, 0x8081, 0x8009,
+            0x008a, 0x0088, 0x8009, 0x000a, 0x808b, 0x008b, 0x8089, 0x8003, 0x8002, 0x0080, 0x800a, 0x000a};
+            protected ushort[] SX = new ushort[25];
+            protected ushort[] E = new ushort[25];
+            protected ushort[] C = new ushort[5];
+
+            public override void init(byte[] k, byte[] npub, int ISAP_rH, int ISAP_rH_SZ)
+            {
+                this.k = k;
+                this.npub = npub;
+                this.ISAP_rH = ISAP_rH;
+                this.ISAP_rH_SZ = ISAP_rH_SZ;
+                k16 = new ushort[k.Length >> 1];
+                byteToushort(k, k16, k16.Length);
+                iv16 = new ushort[npub.Length >> 1];
+                byteToushort(npub, iv16, iv16.Length);
+                reset();
+            }
+
+            public override void reset()
+            {
+                // Init state
+                SX = new ushort[25];
+                E = new ushort[25];
+                C = new ushort[5];
+                isap_rk(ISAP_IV3_16, npub, CRYPTO_NPUBBYTES, SX, ISAP_STATE_SZ_CRYPTO_NPUBBYTES, C);
+                Array.Copy(iv16, 0, SX, 17, 8);
+                PermuteRoundsKX(SX, E, C);
+            }
+
+            protected abstract void PermuteRoundsHX(ushort[] SX, ushort[] E, ushort[] C);
+
+            protected abstract void PermuteRoundsKX(ushort[] SX, ushort[] E, ushort[] C);
+
+            protected abstract void PermuteRoundsBX(ushort[] SX, ushort[] E, ushort[] C);
+
+            protected void ABSORB_MAC(ushort[] SX, byte[] src, int len, ushort[] E, ushort[] C)
+            {
+                int rem_bytes = len;
+                int idx = 0;
+                while (true)
+                {
+                    if (rem_bytes > ISAP_rH_SZ)
+                    {
+                        byteToushortXor(src, SX, ISAP_rH_SZ >> 1);
+                        idx += ISAP_rH_SZ;
+                        rem_bytes -= ISAP_rH_SZ;
+                        PermuteRoundsHX(SX, E, C);
+                    }
+                    else if (rem_bytes == ISAP_rH_SZ)
+                    {
+                        byteToushortXor(src, SX, ISAP_rH_SZ >> 1);
+                        PermuteRoundsHX(SX, E, C);
+                        SX[0] ^= 0x80;
+                        PermuteRoundsHX(SX, E, C);
+                        break;
+                    }
+                    else
+                    {
+                        for (int i = 0; i < rem_bytes; i++)
+                        {
+                            SX[i >> 1] ^= (ushort)((src[idx++] & 0xFFU) << ((i & 1) << 3));
+                        }
+                        SX[rem_bytes >> 1] ^= (ushort)(0x80U << ((rem_bytes & 1) << 3));
+                        PermuteRoundsHX(SX, E, C);
+                        break;
+                    }
+                }
+            }
+
+            public void isap_rk(ushort[] iv16, byte[] y, int ylen, ushort[] out16, int outlen, ushort[] C)
+            {
+                // Init state
+                ushort[] SX = new ushort[25];
+                ushort[] E = new ushort[25];
+                Array.Copy(k16, 0, SX, 0, 8);
+                Array.Copy(iv16, 0, SX, 8, 4);
+                PermuteRoundsKX(SX, E, C);
+                // Absorb all bits of Y
+                for (int i = 0; i < (ylen << 3) - 1; i++)
+                {
+                    SX[0] ^= (ushort)(((y[i >> 3] >> (7 - (i & 7))) & 0x01) << 7);
+                    PermuteRoundsBX(SX, E, C);
+                }
+                SX[0] ^= (ushort)(((y[ylen - 1]) & 0x01) << 7);
+                PermuteRoundsKX(SX, E, C);
+                // Extract K*
+                Array.Copy(SX, 0, out16, 0, outlen == ISAP_STATE_SZ_CRYPTO_NPUBBYTES ? 17 : 8);
+            }
+
+            public override void isap_mac(byte[] ad, int adlen, byte[] c, int clen, byte[] tag, int tagOff)
+            {
+                SX = new ushort[25];
+                // Init state
+                Array.Copy(iv16, 0, SX, 0, 8);
+                Array.Copy(ISAP_IV1_16, 0, SX, 8, 4);
+                PermuteRoundsHX(SX, E, C);
+                // Absorb AD
+                ABSORB_MAC(SX, ad, adlen, E, C);
+                // Domain seperation
+                SX[24] ^= 0x0100;
+                // Absorb C
+                ABSORB_MAC(SX, c, clen, E, C);
+                // Derive K*
+                ushortToByte(SX, tag, tagOff);
+                isap_rk(ISAP_IV2_16, tag, CRYPTO_KEYBYTES, SX, CRYPTO_KEYBYTES, C);
+                // Squeeze tag
+                PermuteRoundsHX(SX, E, C);
+                ushortToByte(SX, tag, tagOff);
+            }
+
+            public override void isap_enc(byte[] m, int mOff, int mlen, byte[] c, int cOff, int clen)
+            {
+                // Squeeze key stream
+                while (true)
+                {
+                    if (mlen >= ISAP_rH_SZ)
+                    {
+                        // Squeeze full lane and continue
+                        for (int i = 0; i < ISAP_rH_SZ; ++i)
+                        {
+                            c[cOff++] = (byte)((SX[i >> 1] >> ((i & 1) << 3)) ^ m[mOff++]);
+                        }
+                        mlen -= ISAP_rH_SZ;
+                        PermuteRoundsKX(SX, E, C);
+                    }
+                    else
+                    {
+                        // Squeeze full or partial lane and stop
+                        for (int i = 0; i < mlen; ++i)
+                        {
+                            c[cOff++] = (byte)((SX[i >> 1] >> ((i & 1) << 3)) ^ m[mOff++]);
+                        }
+                        break;
+                    }
+                }
+            }
+
+            private void byteToushortXor(byte[] input, ushort[] output, int outLen)
+            {
+                for (int i = 0; i < outLen; ++i)
+                {
+                    output[i] ^= Pack.LE_To_UInt16(input, (i << 1));
+                }
+            }
+
+            private void byteToushort(byte[] input, ushort[] output, int outLen)
+            {
+                for (int i = 0; i < outLen; ++i)
+                {
+                    output[i] = Pack.LE_To_UInt16(input, (i << 1));
+                }
+            }
+
+            private void ushortToByte(ushort[] input, byte[] output, int outOff)
+            {
+                for (int i = 0; i < 8; ++i)
+                {
+                    shortToLittleEndian(input[i], output, outOff + (i << 1));
+                }
+            }
+
+            protected void rounds12X(ushort[] SX, ushort[] E, ushort[] C)
+            {
+                prepareThetaX(SX, C);
+                rounds_8_18(SX, E, C);
+            }
+
+            protected void rounds_4_18(ushort[] SX, ushort[] E, ushort[] C)
+            {
+                thetaRhoPiChiIotaPrepareTheta(4, SX, E, C);
+                thetaRhoPiChiIotaPrepareTheta(5, E, SX, C);
+                thetaRhoPiChiIotaPrepareTheta(6, SX, E, C);
+                thetaRhoPiChiIotaPrepareTheta(7, E, SX, C);
+                rounds_8_18(SX, E, C);
+            }
+
+            protected void rounds_8_18(ushort[] SX, ushort[] E, ushort[] C)
+            {
+                thetaRhoPiChiIotaPrepareTheta(8, SX, E, C);
+                thetaRhoPiChiIotaPrepareTheta(9, E, SX, C);
+                thetaRhoPiChiIotaPrepareTheta(10, SX, E, C);
+                thetaRhoPiChiIotaPrepareTheta(11, E, SX, C);
+                rounds_12_18(SX, E, C);
+            }
+
+            protected void rounds_12_18(ushort[] SX, ushort[] E, ushort[] C)
+            {
+                thetaRhoPiChiIotaPrepareTheta(12, SX, E, C);
+                thetaRhoPiChiIotaPrepareTheta(13, E, SX, C);
+                thetaRhoPiChiIotaPrepareTheta(14, SX, E, C);
+                thetaRhoPiChiIotaPrepareTheta(15, E, SX, C);
+                thetaRhoPiChiIotaPrepareTheta(16, SX, E, C);
+                thetaRhoPiChiIotaPrepareTheta(17, E, SX, C);
+                thetaRhoPiChiIotaPrepareTheta(18, SX, E, C);
+                thetaRhoPiChiIota(E, SX, C);
+            }
+
+            protected void prepareThetaX(ushort[] SX, ushort[] C)
+            {
+                C[0] = (ushort)(SX[0] ^ SX[5] ^ SX[10] ^ SX[15] ^ SX[20]);
+                C[1] = (ushort)(SX[1] ^ SX[6] ^ SX[11] ^ SX[16] ^ SX[21]);
+                C[2] = (ushort)(SX[2] ^ SX[7] ^ SX[12] ^ SX[17] ^ SX[22]);
+                C[3] = (ushort)(SX[3] ^ SX[8] ^ SX[13] ^ SX[18] ^ SX[23]);
+                C[4] = (ushort)(SX[4] ^ SX[9] ^ SX[14] ^ SX[19] ^ SX[24]);
+            }
+
+            private ushort ROL16(ushort a, int offset)
+            {
+                return (ushort)(((a & 0xFFFF) << offset) ^ ((a & 0xFFFF) >> (16 - offset)));
+            }
+
+            protected void thetaRhoPiChiIotaPrepareTheta(int i, ushort[] A, ushort[] E, ushort[] C)
+            {
+                ushort Da = (ushort)(C[4] ^ ROL16(C[1], 1));
+                ushort De = (ushort)(C[0] ^ ROL16(C[2], 1));
+                ushort Di = (ushort)(C[1] ^ ROL16(C[3], 1));
+                ushort Do = (ushort)(C[2] ^ ROL16(C[4], 1));
+                ushort Du = (ushort)(C[3] ^ ROL16(C[0], 1));
+
+                ushort Ba = A[0] ^= Da;
+                A[6] ^= De;
+                ushort Be = ROL16(A[6], 12);
+                A[12] ^= Di;
+                ushort Bi = ROL16(A[12], 11);
+                A[18] ^= Do;
+                ushort Bo = ROL16(A[18], 5);
+                A[24] ^= Du;
+                ushort Bu = ROL16(A[24], 14);
+                C[0] = E[0] = (ushort)(Ba ^ ((~Be) & Bi) ^ KeccakF400RoundConstants[i]);
+                C[1] = E[1] = (ushort)(Be ^ ((~Bi) & Bo));
+                C[2] = E[2] = (ushort)(Bi ^ ((~Bo) & Bu));
+                C[3] = E[3] = (ushort)(Bo ^ ((~Bu) & Ba));
+                C[4] = E[4] = (ushort)(Bu ^ ((~Ba) & Be));
+
+                A[3] ^= Do;
+                Ba = ROL16(A[3], 12);
+                A[9] ^= Du;
+                Be = ROL16(A[9], 4);
+                A[10] ^= Da;
+                Bi = ROL16(A[10], 3);
+                A[16] ^= De;
+                Bo = ROL16(A[16], 13);
+                A[22] ^= Di;
+                Bu = ROL16(A[22], 13);
+                E[5] = (ushort)(Ba ^ ((~Be) & Bi));
+                C[0] ^= E[5];
+                E[6] = (ushort)(Be ^ ((~Bi) & Bo));
+                C[1] ^= E[6];
+                E[7] = (ushort)(Bi ^ ((~Bo) & Bu));
+                C[2] ^= E[7];
+                E[8] = (ushort)(Bo ^ ((~Bu) & Ba));
+                C[3] ^= E[8];
+                E[9] = (ushort)(Bu ^ ((~Ba) & Be));
+                C[4] ^= E[9];
+
+                A[1] ^= De;
+                Ba = ROL16(A[1], 1);
+                A[7] ^= Di;
+                Be = ROL16(A[7], 6);
+                A[13] ^= Do;
+                Bi = ROL16(A[13], 9);
+                A[19] ^= Du;
+                Bo = ROL16(A[19], 8);
+                A[20] ^= Da;
+                Bu = ROL16(A[20], 2);
+                E[10] = (ushort)(Ba ^ ((~Be) & Bi));
+                C[0] ^= E[10];
+                E[11] = (ushort)(Be ^ ((~Bi) & Bo));
+                C[1] ^= E[11];
+                E[12] = (ushort)(Bi ^ ((~Bo) & Bu));
+                C[2] ^= E[12];
+                E[13] = (ushort)(Bo ^ ((~Bu) & Ba));
+                C[3] ^= E[13];
+                E[14] = (ushort)(Bu ^ ((~Ba) & Be));
+                C[4] ^= E[14];
+
+                A[4] ^= Du;
+                Ba = ROL16(A[4], 11);
+                A[5] ^= Da;
+                Be = ROL16(A[5], 4);
+                A[11] ^= De;
+                Bi = ROL16(A[11], 10);
+                A[17] ^= Di;
+                Bo = ROL16(A[17], 15);
+                A[23] ^= Do;
+                Bu = ROL16(A[23], 8);
+                E[15] = (ushort)(Ba ^ ((~Be) & Bi));
+                C[0] ^= E[15];
+                E[16] = (ushort)(Be ^ ((~Bi) & Bo));
+                C[1] ^= E[16];
+                E[17] = (ushort)(Bi ^ ((~Bo) & Bu));
+                C[2] ^= E[17];
+                E[18] = (ushort)(Bo ^ ((~Bu) & Ba));
+                C[3] ^= E[18];
+                E[19] = (ushort)(Bu ^ ((~Ba) & Be));
+                C[4] ^= E[19];
+
+                A[2] ^= Di;
+                Ba = ROL16(A[2], 14);
+                A[8] ^= Do;
+                Be = ROL16(A[8], 7);
+                A[14] ^= Du;
+                Bi = ROL16(A[14], 7);
+                A[15] ^= Da;
+                Bo = ROL16(A[15], 9);
+                A[21] ^= De;
+                Bu = ROL16(A[21], 2);
+                E[20] = (ushort)(Ba ^ ((~Be) & Bi));
+                C[0] ^= E[20];
+                E[21] = (ushort)(Be ^ ((~Bi) & Bo));
+                C[1] ^= E[21];
+                E[22] = (ushort)(Bi ^ ((~Bo) & Bu));
+                C[2] ^= E[22];
+                E[23] = (ushort)(Bo ^ ((~Bu) & Ba));
+                C[3] ^= E[23];
+                E[24] = (ushort)(Bu ^ ((~Ba) & Be));
+                C[4] ^= E[24];
+            }
+
+            protected void thetaRhoPiChiIota(ushort[] A, ushort[] E, ushort[] C)
+            {
+                ushort Da = (ushort)(C[4] ^ ROL16(C[1], 1));
+                ushort De = (ushort)(C[0] ^ ROL16(C[2], 1));
+                ushort Di = (ushort)(C[1] ^ ROL16(C[3], 1));
+                ushort Do = (ushort)(C[2] ^ ROL16(C[4], 1));
+                ushort Du = (ushort)(C[3] ^ ROL16(C[0], 1));
+
+                ushort Ba = A[0] ^= Da;
+                A[6] ^= De;
+                ushort Be = ROL16(A[6], 12);
+                A[12] ^= Di;
+                ushort Bi = ROL16(A[12], 11);
+                A[18] ^= Do;
+                ushort Bo = ROL16(A[18], 5);
+                A[24] ^= Du;
+                ushort Bu = ROL16(A[24], 14);
+                E[0] = (ushort)(Ba ^ ((~Be) & Bi) ^ KeccakF400RoundConstants[19]);
+                E[1] = (ushort)(Be ^ ((~Bi) & Bo));
+                E[2] = (ushort)(Bi ^ ((~Bo) & Bu));
+                E[3] = (ushort)(Bo ^ ((~Bu) & Ba));
+                E[4] = (ushort)(Bu ^ ((~Ba) & Be));
+
+                A[3] ^= Do;
+                Ba = ROL16(A[3], 12);
+                A[9] ^= Du;
+                Be = ROL16(A[9], 4);
+                A[10] ^= Da;
+                Bi = ROL16(A[10], 3);
+                A[16] ^= De;
+                Bo = ROL16(A[16], 13);
+                A[22] ^= Di;
+                Bu = ROL16(A[22], 13);
+                E[5] = (ushort)(Ba ^ ((~Be) & Bi));
+                E[6] = (ushort)(Be ^ ((~Bi) & Bo));
+                E[7] = (ushort)(Bi ^ ((~Bo) & Bu));
+                E[8] = (ushort)(Bo ^ ((~Bu) & Ba));
+                E[9] = (ushort)(Bu ^ ((~Ba) & Be));
+
+                A[1] ^= De;
+                Ba = ROL16(A[1], 1);
+                A[7] ^= Di;
+                Be = ROL16(A[7], 6);
+                A[13] ^= Do;
+                Bi = ROL16(A[13], 9);
+                A[19] ^= Du;
+                Bo = ROL16(A[19], 8);
+                A[20] ^= Da;
+                Bu = ROL16(A[20], 2);
+                E[10] = (ushort)(Ba ^ ((~Be) & Bi));
+                E[11] = (ushort)(Be ^ ((~Bi) & Bo));
+                E[12] = (ushort)(Bi ^ ((~Bo) & Bu));
+                E[13] = (ushort)(Bo ^ ((~Bu) & Ba));
+                E[14] = (ushort)(Bu ^ ((~Ba) & Be));
+
+                A[4] ^= Du;
+                Ba = ROL16(A[4], 11);
+                A[5] ^= Da;
+                Be = ROL16(A[5], 4);
+                A[11] ^= De;
+                Bi = ROL16(A[11], 10);
+                A[17] ^= Di;
+                Bo = ROL16(A[17], 15);
+                A[23] ^= Do;
+                Bu = ROL16(A[23], 8);
+                E[15] = (ushort)(Ba ^ ((~Be) & Bi));
+                E[16] = (ushort)(Be ^ ((~Bi) & Bo));
+                E[17] = (ushort)(Bi ^ ((~Bo) & Bu));
+                E[18] = (ushort)(Bo ^ ((~Bu) & Ba));
+                E[19] = (ushort)(Bu ^ ((~Ba) & Be));
+
+                A[2] ^= Di;
+                Ba = ROL16(A[2], 14);
+                A[8] ^= Do;
+                Be = ROL16(A[8], 7);
+                A[14] ^= Du;
+                Bi = ROL16(A[14], 7);
+                A[15] ^= Da;
+                Bo = ROL16(A[15], 9);
+                A[21] ^= De;
+                Bu = ROL16(A[21], 2);
+                E[20] = (ushort)(Ba ^ ((~Be) & Bi));
+                E[21] = (ushort)(Be ^ ((~Bi) & Bo));
+                E[22] = (ushort)(Bi ^ ((~Bo) & Bu));
+                E[23] = (ushort)(Bo ^ ((~Bu) & Ba));
+                E[24] = (ushort)(Bu ^ ((~Ba) & Be));
+            }
+        }
+
+        private class ISAPAEAD_K_128A : ISAPAEAD_K
+        {
+            public ISAPAEAD_K_128A()
+            {
+                ISAP_IV1_16 = new ushort[] { 32769, 400, 272, 2056 };
+                ISAP_IV2_16 = new ushort[] { 32770, 400, 272, 2056 };
+                ISAP_IV3_16 = new ushort[] { 32771, 400, 272, 2056 };
+            }
+
+            protected override void PermuteRoundsHX(ushort[] SX, ushort[] E, ushort[] C)
+            {
+                prepareThetaX(SX, C);
+                rounds_4_18(SX, E, C);
+            }
+
+            protected override void PermuteRoundsKX(ushort[] SX, ushort[] E, ushort[] C)
+            {
+                prepareThetaX(SX, C);
+                rounds_12_18(SX, E, C);
+            }
+
+            protected override void PermuteRoundsBX(ushort[] SX, ushort[] E, ushort[] C)
+            {
+                prepareThetaX(SX, C);
+                thetaRhoPiChiIotaPrepareTheta(19, SX, E, C);
+                Array.Copy(E, 0, SX, 0, E.Length);
+            }
+        }
+
+        private class ISAPAEAD_K_128
+            : ISAPAEAD_K
+        {
+            public ISAPAEAD_K_128()
+            {
+                ISAP_IV1_16 = new ushort[] { 32769, 400, 3092, 3084 };
+                ISAP_IV2_16 = new ushort[] { 32770, 400, 3092, 3084 };
+                ISAP_IV3_16 = new ushort[] { 32771, 400, 3092, 3084 };
+            }
+
+            protected override void PermuteRoundsHX(ushort[] SX, ushort[] E, ushort[] C)
+            {
+                prepareThetaX(SX, C);
+                thetaRhoPiChiIotaPrepareTheta(0, SX, E, C);
+                thetaRhoPiChiIotaPrepareTheta(1, E, SX, C);
+                thetaRhoPiChiIotaPrepareTheta(2, SX, E, C);
+                thetaRhoPiChiIotaPrepareTheta(3, E, SX, C);
+                rounds_4_18(SX, E, C);
+            }
+
+            protected override void PermuteRoundsKX(ushort[] SX, ushort[] E, ushort[] C)
+            {
+                rounds12X(SX, E, C);
+            }
+
+            protected override void PermuteRoundsBX(ushort[] SX, ushort[] E, ushort[] C)
+            {
+                rounds12X(SX, E, C);
+            }
+        }
+
+
+
+        public void Init(bool forEncryption, ICipherParameters param)
+        {
+            this.forEncryption = forEncryption;
+            if (!(param is ParametersWithIV))
+            {
+                throw new ArgumentException(
+                    "ISAP AEAD init parameters must include an IV");
+            }
+
+            ParametersWithIV ivParams = (ParametersWithIV)param;
+
+            byte[] iv = ivParams.GetIV();
+
+            if (iv == null || iv.Length != 16)
+            {
+                throw new ArgumentException(
+                    "ISAP AEAD requires exactly 12 bytes of IV");
+            }
+
+            if (!(ivParams.Parameters is KeyParameter))
+            {
+                throw new ArgumentException(
+                    "ISAP AEAD init parameters must include a key");
+            }
+
+            KeyParameter key = (KeyParameter)ivParams.Parameters;
+            byte[] keyBytes = key.GetKey();
+            if (keyBytes.Length != 16)
+            {
+                throw new ArgumentException(
+                    "ISAP AEAD key must be 128 bits ulong");
+            }
+
+            /**
+             * Initialize variables.
+             */
+            byte[] npub = new byte[iv.Length];
+            byte[] k = new byte[keyBytes.Length];
+            Array.Copy(iv, 0, npub, 0, iv.Length);
+            Array.Copy(keyBytes, 0, k, 0, keyBytes.Length);
+            initialised = true;
+            ISAPAEAD.init(k, npub, ISAP_rH, ISAP_rH_SZ);
+            Reset();
+        }
+
+        public void ProcessAadByte(byte input)
+        {
+            aadData.Write(new byte[] { input }, 0, 1);
+        }
+
+
+        public void ProcessAadBytes(byte[] input, int inOff, int len)
+        {
+            if ((inOff + len) > input.Length)
+            {
+                throw new DataLengthException("input buffer too short" + (forEncryption ? "encryption" : "decryption"));
+            }
+            aadData.Write(input, inOff, len);
+        }
+
+
+        public int ProcessByte(byte input, byte[] output, int outOff)
+        {
+
+            return ProcessBytes(new byte[] { input }, 0, 1, output, outOff);
+        }
+
+
+        public int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
+        {
+            if (!initialised)
+            {
+                throw new ArgumentException("Need call init function before encryption/decryption");
+            }
+            if ((inOff + len) > input.Length)
+            {
+                throw new DataLengthException("input buffer too short");
+            }
+            message.Write(input, inOff, len);
+            if (forEncryption)
+            {
+                if (message.Length >= ISAP_rH_SZ)
+                {
+                    len = (int)message.Length / ISAP_rH_SZ * ISAP_rH_SZ;
+                    if (outOff + len > output.Length)
+                    {
+                        throw new OutputLengthException("output buffer is too short");
+                    }
+                    byte[] enc_input = message.GetBuffer();
+                    ISAPAEAD.isap_enc(enc_input, 0, len, output, outOff, output.Length);
+                    outputStream.Write(output, outOff, len);
+                    int enc_input_len = (int)message.Length;
+                    message.SetLength(0);
+                    message.Write(enc_input, len, enc_input_len - len);
+                    return len;
+                }
+            }
+            return 0;
+        }
+
+
+        public int DoFinal(byte[] output, int outOff)
+        {
+            if (!initialised)
+            {
+                throw new ArgumentException("Need call init function before encryption/decryption");
+            }
+            int len;
+            if (forEncryption)
+            {
+                byte[] enc_input = message.GetBuffer();
+                len = (int)message.Length;
+                if (outOff + len + 16 > output.Length)
+                {
+                    throw new OutputLengthException("output buffer is too short");
+                }
+                ISAPAEAD.isap_enc(enc_input, 0, len, output, outOff, output.Length);
+                outputStream.Write(output, outOff, len);
+                outOff += len;
+                ad = aadData.GetBuffer();
+                c = outputStream.GetBuffer();
+                mac = new byte[16];
+                ISAPAEAD.isap_mac(ad, (int)aadData.Length, c, (int)outputStream.Length, mac, 0);
+                Array.Copy(mac, 0, output, outOff, 16);
+                len += 16;
+            }
+            else
+            {
+                ad = aadData.GetBuffer();
+                int adlen = (int)aadData.Length;
+                c = message.GetBuffer();
+                int clen = (int)message.Length;
+                mac = new byte[16];
+                len = clen - mac.Length;
+                if (len + outOff > output.Length)
+                {
+                    throw new OutputLengthException("output buffer is too short");
+                }
+                ISAPAEAD.isap_mac(ad, adlen, c, len, mac, 0);
+                ISAPAEAD.reset();
+                for (int i = 0; i < 16; ++i)
+                {
+                    if (mac[i] != c[len + i])
+                    {
+                        throw new ArgumentException("Mac does not match");
+                    }
+                }
+                ISAPAEAD.isap_enc(c, 0, len, output, outOff, output.Length);
+            }
+            return len;
+        }
+
+
+        public byte[] GetMac()
+        {
+            return mac;
+        }
+
+
+        public int GetUpdateOutputSize(int len)
+        {
+            return len;
+        }
+
+
+        public int GetOutputSize(int len)
+        {
+            return len + 16;
+        }
+
+
+        public void Reset()
+        {
+            if (!initialised)
+            {
+                throw new ArgumentException("Need call init function before encryption/decryption");
+            }
+            aadData.SetLength(0);
+            ISAPAEAD.reset();
+            message.SetLength(0);
+            outputStream.SetLength(0);
+        }
+
+        private static void shortToLittleEndian(ushort n, byte[] bs, int off)
+        {
+            bs[off] = (byte)(n);
+            bs[++off] = (byte)(n >> 8);
+        }
+
+        public int GetBlockSize()
+        {
+            return ISAP_rH_SZ;
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void ProcessAadBytes(ReadOnlySpan<byte> input)
+        {
+            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 GetKeyBytesSize()
+        {
+            return CRYPTO_KEYBYTES;
+        }
+
+        public int GetIVBytesSize()
+        {
+            return CRYPTO_NPUBBYTES;
+        }
+    }
+}
+
+