summary refs log tree commit diff
path: root/crypto/src
diff options
context:
space:
mode:
authorgefeili <gli@keyfactor.com>2023-02-09 11:31:33 +1030
committergefeili <gli@keyfactor.com>2023-02-09 11:31:33 +1030
commit242b0985e06f83347889c18ead32b41ef3d73e9f (patch)
treeb5f82738d3a1a9c260a1efd93bf857b1436f7003 /crypto/src
parentRefactor AsconEngine (diff)
downloadBouncyCastle.NET-ed25519-242b0985e06f83347889c18ead32b41ef3d73e9f.tar.xz
Add Ascon Hash to the master branch
Diffstat (limited to 'crypto/src')
-rw-r--r--crypto/src/crypto/digests/AsconDigest.cs228
1 files changed, 228 insertions, 0 deletions
diff --git a/crypto/src/crypto/digests/AsconDigest.cs b/crypto/src/crypto/digests/AsconDigest.cs
new file mode 100644
index 000000000..9c77024b8
--- /dev/null
+++ b/crypto/src/crypto/digests/AsconDigest.cs
@@ -0,0 +1,228 @@
+using System;
+using System.IO;
+using Org.BouncyCastle.Crypto.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Digests
+{
+    public class AsconDigest : IDigest
+    {
+        public enum AsconParameters
+        {
+            AsconHash,
+            AsconHashA,
+            AsconXof,
+            AsconXofA,
+        }
+
+        public AsconDigest(AsconParameters parameters)
+        {
+            switch (parameters)
+            {
+                case AsconParameters.AsconHash:
+                    ASCON_PB_ROUNDS = 12;
+                    ASCON_IV = (((ulong)(ASCON_HASH_RATE * 8) << 48) |
+                    ((ulong)(ASCON_PA_ROUNDS) << 40) |
+                    ((ulong)(ASCON_HASH_BYTES * 8)));
+                    algorithmName = "Ascon-Hash";
+                    break;
+                case AsconParameters.AsconHashA:
+                    ASCON_PB_ROUNDS = 8;
+                    ASCON_IV = (((ulong)(ASCON_HASH_RATE * 8) << 48) |
+                    ((ulong)(ASCON_PA_ROUNDS) << 40) |
+                    ((ulong)(ASCON_PA_ROUNDS - ASCON_PB_ROUNDS) << 32) |
+                    ((ulong)(ASCON_HASH_BYTES * 8)));
+                    algorithmName = "Ascon-HashA";
+                    break;
+                case AsconParameters.AsconXof:
+                    ASCON_PB_ROUNDS = 12;
+                    ASCON_IV = (((ulong)(ASCON_HASH_RATE * 8) << 48) |
+                    ((ulong)(ASCON_PA_ROUNDS) << 40));
+                    algorithmName = "Ascon-Xof";
+                    break;
+                case AsconParameters.AsconXofA:
+                    ASCON_PB_ROUNDS = 8;
+                    ASCON_IV = (((ulong)(ASCON_HASH_RATE * 8) << 48) |
+                    ((ulong)(ASCON_PA_ROUNDS) << 40) |
+                    ((ulong)(ASCON_PA_ROUNDS - ASCON_PB_ROUNDS) << 32));
+                    algorithmName = "Ascon-XofA";
+                    break;
+                default:
+                    throw new ArgumentException("Invalid parameter settings for Ascon Hash");
+            }
+        }
+
+        private string algorithmName;
+
+        private readonly MemoryStream buffer = new MemoryStream();
+        private ulong x0;
+        private ulong x1;
+        private ulong x2;
+        private ulong x3;
+        private ulong x4;
+        private readonly int CRYPTO_BYTES = 32;
+        private readonly ulong ASCON_IV;
+        private readonly int ASCON_HASH_RATE = 8;
+        private readonly int ASCON_PA_ROUNDS = 12;
+        private int ASCON_PB_ROUNDS;
+
+
+        private uint ASCON_HASH_BYTES = 32;
+
+        public string AlgorithmName => algorithmName;
+
+        private ulong ROR(ulong x, int n)
+        {
+            return x >> n | x << (64 - n);
+        }
+
+        private void ROUND(ulong C)
+        {
+            ulong t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C));
+            ulong t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3));
+            ulong t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4);
+            ulong t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4));
+            ulong t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1);
+            x0 = t0 ^ ROR(t0, 19) ^ ROR(t0, 28);
+            x1 = t1 ^ ROR(t1, 39) ^ ROR(t1, 61);
+            x2 = ~(t2 ^ ROR(t2, 1) ^ ROR(t2, 6));
+            x3 = t3 ^ ROR(t3, 10) ^ ROR(t3, 17);
+            x4 = t4 ^ ROR(t4, 7) ^ ROR(t4, 41);
+        }
+
+        private void P(int nr)
+        {
+            if (nr == 12)
+            {
+                ROUND(0xf0UL);
+                ROUND(0xe1UL);
+                ROUND(0xd2UL);
+                ROUND(0xc3UL);
+            }
+            if (nr >= 8)
+            {
+                ROUND(0xb4UL);
+                ROUND(0xa5UL);
+            }
+            ROUND(0x96UL);
+            ROUND(0x87UL);
+            ROUND(0x78UL);
+            ROUND(0x69UL);
+            ROUND(0x5aUL);
+            ROUND(0x4bUL);
+        }
+
+        private ulong PAD(int i)
+        {
+            return 0x80UL << (56 - (i << 3));
+        }
+
+        private ulong LOADBYTES(byte[] bytes, int inOff, int n)
+        {
+            ulong x = 0;
+            for (int i = 0; i < n; ++i)
+            {
+                x |= (bytes[i + inOff] & 0xFFUL) << ((7 - i) << 3);
+            }
+            return x;
+        }
+
+        private void STOREBYTES(byte[] bytes, int inOff, ulong w, int n)
+        {
+            for (int i = 0; i < n; ++i)
+            {
+                bytes[i + inOff] = (byte)(w >> ((7 - i) << 3));
+            }
+        }
+
+        public int GetDigestSize()
+        {
+            return CRYPTO_BYTES;
+        }
+
+
+        public void Update(byte input)
+        {
+            buffer.Write(new byte[] { input }, 0, 1);
+        }
+
+
+        public void BlockUpdate(byte[] input, int inOff, int len)
+        {
+            if ((inOff + len) > input.Length)
+            {
+                throw new DataLengthException("input buffer too ushort");
+            }
+            buffer.Write(input, inOff, len);
+        }
+
+
+        public int DoFinal(byte[] output, int outOff)
+        {
+            if (CRYPTO_BYTES + outOff > output.Length)
+            {
+                throw new OutputLengthException("output buffer is too ushort");
+            }
+            byte[] input = buffer.GetBuffer();
+            int len = (int)buffer.Length;
+            int inOff = 0;
+            /* initialize */
+            x0 = ASCON_IV;
+            x1 = 0;
+            x2 = 0;
+            x3 = 0;
+            x4 = 0;
+            P(ASCON_PA_ROUNDS);
+            /* absorb full plaintext blocks */
+            while (len >= ASCON_HASH_RATE)
+            {
+                x0 ^= LOADBYTES(input, inOff, 8);
+                P(ASCON_PB_ROUNDS);
+                inOff += ASCON_HASH_RATE;
+                len -= ASCON_HASH_RATE;
+            }
+            /* absorb readonly plaintext block */
+            x0 ^= LOADBYTES(input, inOff, len);
+            x0 ^= PAD(len);
+            P(ASCON_PA_ROUNDS);
+            /* squeeze full output blocks */
+            len = CRYPTO_BYTES;
+            while (len > ASCON_HASH_RATE)
+            {
+                STOREBYTES(output, outOff, x0, 8);
+                P(ASCON_PB_ROUNDS);
+                outOff += ASCON_HASH_RATE;
+                len -= ASCON_HASH_RATE;
+            }
+            /* squeeze readonly output block */
+            STOREBYTES(output, outOff, x0, len);
+            return CRYPTO_BYTES;
+        }
+
+
+        public void Reset()
+        {
+            buffer.SetLength(0);
+        }
+
+        public int GetByteLength()
+        {
+            throw new NotImplementedException();
+        }
+
+#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
+    }
+}
+