summary refs log tree commit diff
path: root/crypto/src
diff options
context:
space:
mode:
authorgefeili <gli@keyfactor.com>2023-01-27 17:40:35 +1030
committergefeili <gli@keyfactor.com>2023-01-27 17:40:35 +1030
commit8532622aaddc27d3dded733a56580a7d286ba9e9 (patch)
tree0222fe8ac522bd4033c7786f3ff27001104fcc46 /crypto/src
parentMIsc. cleanup (diff)
downloadBouncyCastle.NET-ed25519-8532622aaddc27d3dded733a56580a7d286ba9e9.tar.xz
Add Xoodyak to the master branch
Diffstat (limited to 'crypto/src')
-rw-r--r--crypto/src/crypto/digests/XoodyakDigest.cs207
-rw-r--r--crypto/src/crypto/engines/XoodyakEngine.cs450
2 files changed, 657 insertions, 0 deletions
diff --git a/crypto/src/crypto/digests/XoodyakDigest.cs b/crypto/src/crypto/digests/XoodyakDigest.cs
new file mode 100644
index 000000000..cf1afcc10
--- /dev/null
+++ b/crypto/src/crypto/digests/XoodyakDigest.cs
@@ -0,0 +1,207 @@
+using System;
+using System.IO;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+    public class XoodyakDigest : IDigest
+    {
+        private byte[] state;
+        private int phase;
+        private MODE mode;
+        private int Rabsorb;
+        private const int f_bPrime = 48;
+        private const int Rkout = 24;
+        private const int PhaseDown = 1;
+        private const int PhaseUp = 2;
+        private const int NLANES = 12;
+        private const int NROWS = 3;
+        private const int NCOLUMS = 4;
+        private const int MAXROUNDS = 12;
+        private const int TAGLEN = 16;
+        private const int Rhash = 16;
+        const int Rkin = 44;
+        private readonly uint[] RC = {0x00000058, 0x00000038, 0x000003C0, 0x000000D0, 0x00000120, 0x00000014, 0x00000060,
+        0x0000002C, 0x00000380, 0x000000F0, 0x000001A0, 0x00000012};
+        private MemoryStream buffer = new MemoryStream();
+        enum MODE
+        {
+            ModeHash,
+            ModeKeyed
+        }
+
+        public XoodyakDigest()
+        {
+            state = new byte[48];
+            Reset();
+        }
+
+        public string AlgorithmName => "Xoodyak Hash";
+
+        private void Up(byte[] Yi, int YiOff, int YiLen, uint Cu)
+        {
+            if (mode != MODE.ModeHash)
+            {
+                state[f_bPrime - 1] ^= (byte)Cu;
+            }
+            uint[] a = new uint[NLANES];
+            Pack.LE_To_UInt32(state, 0, a, 0, a.Length);
+            uint x, y;
+            uint[] b = new uint[NLANES];
+            uint[] p = new uint[NCOLUMS];
+            uint[] e = new uint[NCOLUMS];
+            for (int i = 0; i < MAXROUNDS; ++i)
+            {
+                /* Theta: Column Parity Mixer */
+                for (x = 0; x < NCOLUMS; ++x)
+                {
+                    p[x] = a[index(x, 0)] ^ a[index(x, 1)] ^ a[index(x, 2)];
+                }
+                for (x = 0; x < NCOLUMS; ++x)
+                {
+                    y = p[(x + 3) & 3];
+                    e[x] = ROTL32(y, 5) ^ ROTL32(y, 14);
+                }
+                for (x = 0; x < NCOLUMS; ++x)
+                {
+                    for (y = 0; y < NROWS; ++y)
+                    {
+                        a[index(x, y)] ^= e[x];
+                    }
+                }
+                /* Rho-west: plane shift */
+                for (x = 0; x < NCOLUMS; ++x)
+                {
+                    b[index(x, 0)] = a[index(x, 0)];
+                    b[index(x, 1)] = a[index(x + 3, 1)];
+                    b[index(x, 2)] = ROTL32(a[index(x, 2)], 11);
+                }
+                /* Iota: round ant */
+                b[0] ^= RC[i];
+                /* Chi: non linear layer */
+                for (x = 0; x < NCOLUMS; ++x)
+                {
+                    for (y = 0; y < NROWS; ++y)
+                    {
+                        a[index(x, y)] = b[index(x, y)] ^ (~b[index(x, y + 1)] & b[index(x, y + 2)]);
+                    }
+                }
+                /* Rho-east: plane shift */
+                for (x = 0; x < NCOLUMS; ++x)
+                {
+                    b[index(x, 0)] = a[index(x, 0)];
+                    b[index(x, 1)] = ROTL32(a[index(x, 1)], 1);
+                    b[index(x, 2)] = ROTL32(a[index(x + 2, 2)], 8);
+                }
+                Array.Copy(b, 0, a, 0, NLANES);
+            }
+            Pack.UInt32_To_LE(a, 0, a.Length, state, 0);
+            phase = PhaseUp;
+            if (Yi != null)
+            {
+                Array.Copy(state, 0, Yi, YiOff, YiLen);
+            }
+        }
+
+        void Down(byte[] Xi, int XiOff, int XiLen, uint Cd)
+        {
+            for (int i = 0; i < XiLen; i++)
+            {
+                state[i] ^= Xi[XiOff++];
+            }
+            state[XiLen] ^= 0x01;
+            state[f_bPrime - 1] ^= (byte)((mode == MODE.ModeHash) ? (Cd & 0x01) : Cd);
+            phase = PhaseDown;
+        }
+
+        private uint index(uint x, uint y)
+        {
+            return (((y % NROWS) * NCOLUMS) + ((x) % NCOLUMS));
+        }
+
+        private uint ROTL32(uint a, int offset)
+        {
+            return (a << (offset & 31)) ^ (a >> ((32 - (offset)) & 31));
+        }
+
+        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);
+        }
+
+        public int DoFinal(byte[] output, int outOff)
+        {
+            if (32 + outOff > output.Length)
+            {
+                throw new OutputLengthException("output buffer is too short");
+            }
+            byte[] input = buffer.GetBuffer();
+            int inLen = (int)buffer.Length;
+            int inOff = 0;
+            uint Cd = 0x03;
+            int splitLen;
+            do
+            {
+                if (phase != PhaseUp)
+                {
+                    Up(null, 0, 0, 0);
+                }
+                splitLen = System.Math.Min(inLen, Rabsorb);
+                Down(input, inOff, splitLen, Cd);
+                Cd = 0;
+                inOff += splitLen;
+                inLen -= splitLen;
+            }
+            while (inLen != 0);
+            Up(output, outOff, TAGLEN, 0x40);
+            Down(null, 0, 0, 0);
+            Up(output, outOff + TAGLEN, TAGLEN, 0);
+            return 32;
+        }
+
+        public int GetByteLength()
+        {
+            throw new NotImplementedException();
+        }
+
+        public int GetDigestSize()
+        {
+            return 32;
+        }
+
+        public void Reset()
+        {
+            for (int i = 0; i < state.Length; ++i)
+            {
+                state[i] = 0;
+            }
+            phase = PhaseUp;
+            mode = MODE.ModeHash;
+            Rabsorb = Rhash;
+            buffer.SetLength(0);
+        }
+
+        public void Update(byte input)
+        {
+            buffer.Write(new byte[] { input }, 0, 1);
+        }
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int DoFinal(Span<byte> output)
+        {
+            byte[] rv = new byte[32];
+            int rlt = DoFinal(rv, 0);
+            rv.AsSpan(0, 32).CopyTo(output);
+            return rlt;
+        }
+
+        public void BlockUpdate(ReadOnlySpan<byte> input)
+        {
+            buffer.Write(input.ToArray(), 0, input.Length);
+        }
+#endif
+    }
+}
diff --git a/crypto/src/crypto/engines/XoodyakEngine.cs b/crypto/src/crypto/engines/XoodyakEngine.cs
new file mode 100644
index 000000000..fb4f074ee
--- /dev/null
+++ b/crypto/src/crypto/engines/XoodyakEngine.cs
@@ -0,0 +1,450 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
+/**
+* Xoodyak v1, https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/xoodyak-spec-final.pdf
+* <p>
+* Xoodyak with reference to C Reference Impl from: https://github.com/XKCP/XKCP
+* </p>
+*/
+namespace Org.BouncyCastle.Crypto.Engines
+{
+    public sealed class XoodyakEngine : IAeadBlockCipher
+    {
+        private bool forEncryption;
+        private byte[] state;
+        private int phase;
+        private MODE mode;
+        private int Rabsorb;
+        private const int f_bPrime = 48;
+        private const int Rkout = 24;
+        private byte[] K;
+        private byte[] iv;
+        private const int PhaseDown = 1;
+        private const int PhaseUp = 2;
+        private const int NLANES = 12;
+        private const int NROWS = 3;
+        private const int NCOLUMS = 4;
+        private const int MAXROUNDS = 12;
+        private const int TAGLEN = 16;
+        const int Rkin = 44;
+        private byte[] tag;
+        private readonly uint[] RC = {0x00000058, 0x00000038, 0x000003C0, 0x000000D0, 0x00000120, 0x00000014, 0x00000060,
+        0x0000002C, 0x00000380, 0x000000F0, 0x000001A0, 0x00000012};
+        private bool aadFinished;
+        private bool encrypted;
+        private bool initialised = false;
+        public string AlgorithmName => "Xoodak AEAD";
+
+        public IBlockCipher UnderlyingCipher => throw new NotImplementedException();
+
+        private MemoryStream aadData = new MemoryStream();
+        private MemoryStream message = new MemoryStream();
+
+        enum MODE
+        {
+            ModeHash,
+            ModeKeyed
+        }
+
+        public void Init(bool forEncryption, ICipherParameters param)
+        {
+            this.forEncryption = forEncryption;
+            if (!(param is ParametersWithIV))
+            {
+                throw new ArgumentException("Xoodyak init parameters must include an IV");
+            }
+            ParametersWithIV ivParams = (ParametersWithIV)param;
+            iv = ivParams.GetIV();
+            if (iv == null || iv.Length != 16)
+            {
+                throw new ArgumentException("Xoodyak requires exactly 16 bytes of IV");
+            }
+            if (!(ivParams.Parameters is KeyParameter))
+            {
+                throw new ArgumentException("Xoodyak init parameters must include a key");
+            }
+            KeyParameter key = (KeyParameter)ivParams.Parameters;
+            K = key.GetKey();
+            if (K.Length != 16)
+            {
+                throw new ArgumentException("Xoodyak key must be 128 bits long");
+            }
+            state = new byte[48];
+            tag = new byte[TAGLEN];
+            initialised = true;
+            reset(false);
+        }
+
+        public void ProcessAadByte(byte input)
+        {
+            if (aadFinished)
+            {
+                throw new ArgumentException("AAD cannot be added after reading a full block(" + GetBlockSize() +
+                    " bytes) of input for " + (forEncryption ? "encryption" : "decryption"));
+            }
+            aadData.Write(new byte[] { input }, 0, 1);
+        }
+
+
+        public void ProcessAadBytes(byte[] input, int inOff, int len)
+        {
+            if (aadFinished)
+            {
+                throw new ArgumentException("AAD cannot be added after reading a full block(" + GetBlockSize() +
+                    " bytes) of input for " + (forEncryption ? "encryption" : "decryption"));
+            }
+            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);
+        }
+
+        private void processAAD()
+        {
+            if (!aadFinished)
+            {
+                byte[] ad = aadData.GetBuffer();
+                AbsorbAny(ad, 0, (int)aadData.Length, Rabsorb, 0x03);
+                aadFinished = true;
+            }
+        }
+
+        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 (mode != MODE.ModeKeyed)
+            {
+                throw new ArgumentException("Xoodyak has not been initialised");
+            }
+            if (inOff + len > input.Length)
+            {
+                throw new DataLengthException("input buffer too short");
+            }
+            message.Write(input, inOff, len);
+            int blockLen = (int)message.Length - (forEncryption ? 0 : TAGLEN);
+            if (blockLen >= GetBlockSize())
+            {
+                byte[] blocks = message.GetBuffer();
+                len = blockLen / GetBlockSize() * GetBlockSize();
+                if (len + outOff > output.Length)
+                {
+                    throw new OutputLengthException("output buffer is too short");
+                }
+                processAAD();
+                encrypt(blocks, 0, len, output, outOff);
+                int messageLen = (int)message.Length;
+                message.SetLength(0);
+                message.Write(blocks, len, messageLen - len);
+                return len;
+            }
+            return 0;
+        }
+
+        private int encrypt(byte[] input, int inOff, int len, byte[] output, int outOff)
+        {
+            int IOLen = len;
+            int splitLen;
+            byte[] P = new byte[Rkout];
+            uint Cu = encrypted ? 0u : 0x80u;
+            while (IOLen != 0 || !encrypted)
+            {
+                splitLen = System.Math.Min(IOLen, Rkout); /* use Rkout instead of Rsqueeze, this function is only called in keyed mode */
+                if (forEncryption)
+                {
+                    Array.Copy(input, inOff, P, 0, splitLen);
+                }
+                Up(null, 0, Cu); /* Up without extract */
+                /* Extract from Up and Add */
+                for (int i = 0; i < splitLen; i++)
+                {
+                    output[outOff + i] = (byte)(input[inOff++] ^ state[i]);
+                }
+                if (forEncryption)
+                {
+                    Down(P, 0, splitLen, 0x00);
+                }
+                else
+                {
+                    Down(output, outOff, splitLen, 0x00);
+                }
+                Cu = 0x00;
+                outOff += splitLen;
+                IOLen -= splitLen;
+                encrypted = true;
+            }
+            return len;
+        }
+
+
+        public int DoFinal(byte[] output, int outOff)
+        {
+            if (!initialised)
+            {
+                throw new ArgumentException("Need call init function before encryption/decryption");
+            }
+
+            byte[] blocks = message.GetBuffer();
+            int len = (int)message.Length;
+            if ((forEncryption && len + TAGLEN + outOff > output.Length) ||
+                (!forEncryption && len - TAGLEN + outOff > output.Length))
+            {
+                throw new OutputLengthException("output buffer too short");
+            }
+            processAAD();
+            int rv = 0;
+            if (forEncryption)
+            {
+                encrypt(blocks, 0, len, output, outOff);
+                outOff += len;
+                tag = new byte[TAGLEN];
+                Up(tag, TAGLEN, 0x40);
+                Array.Copy(tag, 0, output, outOff, TAGLEN);
+                rv = len + TAGLEN;
+            }
+            else
+            {
+                int inOff = len - TAGLEN;
+                rv = inOff;
+                encrypt(blocks, 0, inOff, output, outOff);
+                tag = new byte[TAGLEN];
+                Up(tag, TAGLEN, 0x40);
+                for (int i = 0; i < TAGLEN; ++i)
+                {
+                    if (tag[i] != blocks[inOff++])
+                    {
+                        throw new ArgumentException("Mac does not match");
+                    }
+                }
+            }
+            reset(false);
+            return rv;
+        }
+
+
+        public byte[] GetMac()
+        {
+            return tag;
+        }
+
+
+        public int GetUpdateOutputSize(int len)
+        {
+            return len;
+        }
+
+
+        public int GetOutputSize(int len)
+        {
+            return len + TAGLEN;
+        }
+
+
+        public void Reset()
+        {
+            if (!initialised)
+            {
+                throw new ArgumentException("Need call init function before encryption/decryption");
+            }
+            reset(true);
+        }
+
+        private void reset(bool clearMac)
+        {
+            if (clearMac)
+            {
+                tag = null;
+            }
+            Arrays.Fill(state, (byte)0);
+            aadFinished = false;
+            encrypted = false;
+            phase = PhaseUp;
+            message.SetLength(0);
+            aadData.SetLength(0);
+            //Absorb key
+            int KLen = K.Length;
+            int IDLen = iv.Length;
+            byte[] KID = new byte[Rkin];
+            mode = MODE.ModeKeyed;
+            Rabsorb = Rkin;
+            Array.Copy(K, 0, KID, 0, KLen);
+            Array.Copy(iv, 0, KID, KLen, IDLen);
+            KID[KLen + IDLen] = (byte)IDLen;
+            AbsorbAny(KID, 0, KLen + IDLen + 1, Rabsorb, 0x02);
+        }
+
+        private void AbsorbAny(byte[] X, int Xoff, int XLen, int r, uint Cd)
+        {
+            int splitLen;
+            do
+            {
+                if (phase != PhaseUp)
+                {
+                    Up(null, 0, 0);
+                }
+                splitLen = System.Math.Min(XLen, r);
+                Down(X, Xoff, splitLen, Cd);
+                Cd = 0;
+                Xoff += splitLen;
+                XLen -= splitLen;
+            }
+            while (XLen != 0);
+        }
+
+        private void Up(byte[] Yi, int YiLen, uint Cu)
+        {
+            if (mode != MODE.ModeHash)
+            {
+                state[f_bPrime - 1] ^= (byte)Cu;
+            }
+            uint[] a = new uint[NLANES];
+            Pack.LE_To_UInt32(state, 0, a, 0, a.Length);
+            uint x, y;
+            uint[] b = new uint[NLANES];
+            uint[] p = new uint[NCOLUMS];
+            uint[] e = new uint[NCOLUMS];
+            for (int i = 0; i < MAXROUNDS; ++i)
+            {
+                /* Theta: Column Parity Mixer */
+                for (x = 0; x < NCOLUMS; ++x)
+                {
+                    p[x] = a[index(x, 0)] ^ a[index(x, 1)] ^ a[index(x, 2)];
+                }
+                for (x = 0; x < NCOLUMS; ++x)
+                {
+                    y = p[(x + 3) & 3];
+                    e[x] = ROTL32(y, 5) ^ ROTL32(y, 14);
+                }
+                for (x = 0; x < NCOLUMS; ++x)
+                {
+                    for (y = 0; y < NROWS; ++y)
+                    {
+                        a[index(x, y)] ^= e[x];
+                    }
+                }
+                /* Rho-west: plane shift */
+                for (x = 0; x < NCOLUMS; ++x)
+                {
+                    b[index(x, 0)] = a[index(x, 0)];
+                    b[index(x, 1)] = a[index(x + 3, 1)];
+                    b[index(x, 2)] = ROTL32(a[index(x, 2)], 11);
+                }
+                /* Iota: round ant */
+                b[0] ^= RC[i];
+                /* Chi: non linear layer */
+                for (x = 0; x < NCOLUMS; ++x)
+                {
+                    for (y = 0; y < NROWS; ++y)
+                    {
+                        a[index(x, y)] = b[index(x, y)] ^ (~b[index(x, y + 1)] & b[index(x, y + 2)]);
+                    }
+                }
+                /* Rho-east: plane shift */
+                for (x = 0; x < NCOLUMS; ++x)
+                {
+                    b[index(x, 0)] = a[index(x, 0)];
+                    b[index(x, 1)] = ROTL32(a[index(x, 1)], 1);
+                    b[index(x, 2)] = ROTL32(a[index(x + 2, 2)], 8);
+                }
+                Array.Copy(b, 0, a, 0, NLANES);
+            }
+            Pack.UInt32_To_LE(a, 0, a.Length, state, 0);
+            phase = PhaseUp;
+            if (Yi != null)
+            {
+                Array.Copy(state, 0, Yi, 0, YiLen);
+            }
+        }
+
+        void Down(byte[] Xi, int XiOff, int XiLen, uint Cd)
+        {
+            for (int i = 0; i < XiLen; i++)
+            {
+                state[i] ^= Xi[XiOff++];
+            }
+            state[XiLen] ^= 0x01;
+            state[f_bPrime - 1] ^= (byte)((mode == MODE.ModeHash) ? (Cd & 0x01) : Cd);
+            phase = PhaseDown;
+        }
+
+        private uint index(uint x, uint y)
+        {
+            return (((y % NROWS) * NCOLUMS) + ((x) % NCOLUMS));
+        }
+
+        private uint ROTL32(uint a, int offset)
+        {
+            return (a << (offset & 31)) ^ (a >> ((32 - (offset)) & 31));
+        }
+
+        public int GetBlockSize()
+        {
+            return Rkout;
+        }
+
+        public int GetKeyBytesSize()
+        {
+            return 16;
+        }
+
+        public int GetIVBytesSize()
+        {
+            return 16;
+        }
+
+#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 - 16];
+            }
+            int len = DoFinal(rv, 0);
+            rv.AsSpan(0, len).CopyTo(output);
+            return rv.Length;
+        }
+
+#endif
+
+    }
+}
\ No newline at end of file