summary refs log tree commit diff
path: root/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'crypto')
-rw-r--r--crypto/src/crypto/digests/SparkleDigest.cs54
-rw-r--r--crypto/test/src/crypto/test/SparkleTest.cs129
2 files changed, 152 insertions, 31 deletions
diff --git a/crypto/src/crypto/digests/SparkleDigest.cs b/crypto/src/crypto/digests/SparkleDigest.cs
index bd03fdf0d..5cc438f88 100644
--- a/crypto/src/crypto/digests/SparkleDigest.cs
+++ b/crypto/src/crypto/digests/SparkleDigest.cs
@@ -21,6 +21,10 @@ namespace Org.BouncyCastle.Crypto.Digests
             ESCH384
         }
 
+        private const int RATE_BITS = 128;
+        private const int RATE_BYTES = 16;
+        private const int RATE_UINTS = 4;
+
         private static readonly uint[] RCON = { 0xB7E15162U, 0xBF715880U, 0x38B4DA56U, 0x324E7738U, 0xBB1185EBU,
             0x4F7C7B57U, 0xCFBFA1C8U, 0xC2B3293DU };
 
@@ -31,40 +35,31 @@ namespace Org.BouncyCastle.Crypto.Digests
         private readonly int SPARKLE_STEPS_SLIM;
         private readonly int SPARKLE_STEPS_BIG;
         private readonly int STATE_BRANS;
-        private readonly int STATE_WORDS;
-        private readonly int RATE_WORDS;
-        private readonly int RATE_BYTES;
+        private readonly int STATE_UINTS;
 
         public SparkleDigest(SparkleParameters sparkleParameters)
         {
-            int ESCH_DIGEST_LEN;
-            int SPARKLE_STATE;
-            int SPARKLE_RATE = 128;
             switch (sparkleParameters)
             {
             case SparkleParameters.ESCH256:
-                ESCH_DIGEST_LEN = 256;
-                SPARKLE_STATE = 384;
+                algorithmName = "ESCH-256";
+                DIGEST_BYTES = 32;
                 SPARKLE_STEPS_SLIM = 7;
                 SPARKLE_STEPS_BIG = 11;
-                algorithmName = "ESCH-256";
+                STATE_UINTS = 12;
                 break;
             case SparkleParameters.ESCH384:
-                ESCH_DIGEST_LEN = 384;
-                SPARKLE_STATE = 512;
+                algorithmName = "ESCH-384";
+                DIGEST_BYTES = 48;
                 SPARKLE_STEPS_SLIM = 8;
                 SPARKLE_STEPS_BIG = 12;
-                algorithmName = "ESCH-384";
+                STATE_UINTS = 16;
                 break;
             default:
                 throw new ArgumentException("Invalid definition of SCHWAEMM instance");
             }
-            STATE_BRANS = SPARKLE_STATE >> 6;
-            STATE_WORDS = SPARKLE_STATE >> 5;
-            RATE_WORDS = SPARKLE_RATE >> 5;
-            RATE_BYTES = SPARKLE_RATE >> 3;
-            DIGEST_BYTES = ESCH_DIGEST_LEN >> 3;
-            state = new uint[STATE_WORDS];
+            STATE_BRANS = STATE_UINTS >> 1;
+            state = new uint[STATE_UINTS];
         }
 
         public string AlgorithmName => algorithmName;
@@ -106,19 +101,19 @@ namespace Org.BouncyCastle.Crypto.Digests
                 // addition of a buffer block to the state
                 tmpx = 0;
                 tmpy = 0;
-                for (i = 0; i < RATE_WORDS; i += 2)
+                for (i = 0; i < RATE_UINTS; i += 2)
                 {
                     tmpx ^= in32[i + (inOff >> 2)];
                     tmpy ^= in32[i + 1 + (inOff >> 2)];
                 }
                 tmpx = ELL(tmpx);
                 tmpy = ELL(tmpy);
-                for (i = 0; i < RATE_WORDS; i += 2)
+                for (i = 0; i < RATE_UINTS; i += 2)
                 {
                     state[i] ^= (in32[i + (inOff >> 2)] ^ tmpy);
                     state[i + 1] ^= (in32[i + 1 + (inOff >> 2)] ^ tmpx);
                 }
-                for (i = RATE_WORDS; i < (STATE_WORDS / 2); i += 2)
+                for (i = RATE_UINTS; i < (STATE_UINTS / 2); i += 2)
                 {
                     state[i] ^= tmpy;
                     state[i + 1] ^= tmpx;
@@ -132,7 +127,7 @@ namespace Org.BouncyCastle.Crypto.Digests
             // addition of constant M1 or M2 to the state
             state[STATE_BRANS - 1] ^= ((inlen < RATE_BYTES) ? (1u << 24) : (1u << 25));
             // addition of last msg block (incl. padding)
-            uint[] buffer = new uint[RATE_WORDS];
+            uint[] buffer = new uint[RATE_UINTS];
             for (i = 0; i < inlen; ++i)
             {
                 buffer[i >> 2] |= (input[inOff++] & 0xffu) << ((i & 3) << 3);
@@ -143,38 +138,37 @@ namespace Org.BouncyCastle.Crypto.Digests
             }
             tmpx = 0;
             tmpy = 0;
-            for (i = 0; i < RATE_WORDS; i += 2)
+            for (i = 0; i < RATE_UINTS; i += 2)
             {
                 tmpx ^= buffer[i];
                 tmpy ^= buffer[i + 1];
             }
             tmpx = ELL(tmpx);
             tmpy = ELL(tmpy);
-            for (i = 0; i < RATE_WORDS; i += 2)
+            for (i = 0; i < RATE_UINTS; i += 2)
             {
                 state[i] ^= (buffer[i] ^ tmpy);
                 state[i + 1] ^= (buffer[i + 1] ^ tmpx);
             }
-            for (i = RATE_WORDS; i < (STATE_WORDS / 2); i += 2)
+            for (i = RATE_UINTS; i < (STATE_UINTS / 2); i += 2)
             {
                 state[i] ^= tmpy;
                 state[i + 1] ^= tmpx;
             }
             // execute SPARKLE with big number of steps
             sparkle_opt(state, STATE_BRANS, SPARKLE_STEPS_BIG);
-            Pack.UInt32_To_LE(state, 0, RATE_WORDS, output, outOff);
+            Pack.UInt32_To_LE(state, 0, RATE_UINTS, output, outOff);
             int outlen = RATE_BYTES;
             outOff += RATE_BYTES;
             while (outlen < DIGEST_BYTES)
             {
                 sparkle_opt(state, STATE_BRANS, SPARKLE_STEPS_SLIM);
-                Pack.UInt32_To_LE(state, 0, RATE_WORDS, output, outOff);
+                Pack.UInt32_To_LE(state, 0, RATE_UINTS, output, outOff);
                 outlen += RATE_BYTES;
                 outOff += RATE_BYTES;
             }
+            Reset();
             return DIGEST_BYTES;
-
-            // TODO Reset?
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
@@ -182,8 +176,6 @@ namespace Org.BouncyCastle.Crypto.Digests
         {
             byte[] rv = new byte[DIGEST_BYTES];
             DoFinal(rv, 0);
-            // TODO Remove duplicate if added in other DoFinal
-            Reset();
             rv.AsSpan(0, rv.Length).CopyTo(output);
             return DIGEST_BYTES;
         }
diff --git a/crypto/test/src/crypto/test/SparkleTest.cs b/crypto/test/src/crypto/test/SparkleTest.cs
new file mode 100644
index 000000000..b98a6aedf
--- /dev/null
+++ b/crypto/test/src/crypto/test/SparkleTest.cs
@@ -0,0 +1,129 @@
+using System.Collections.Generic;
+using System.IO;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+    [TestFixture]
+    public class SparkleTest
+    {
+        [Test, Explicit]
+        public void BenchDigest256()
+        {
+            ImplBenchDigest(SparkleDigest.SparkleParameters.ESCH256);
+        }
+
+        [Test, Explicit]
+        public void BenchDigest384()
+        {
+            ImplBenchDigest(SparkleDigest.SparkleParameters.ESCH384);
+        }
+
+        [Test]
+        public void TestExceptionsDigest256()
+        {
+            ImplTestExceptionsDigest(SparkleDigest.SparkleParameters.ESCH256, 32);
+        }
+
+        [Test]
+        public void TestExceptionsDigest384()
+        {
+            ImplTestExceptionsDigest(SparkleDigest.SparkleParameters.ESCH384, 48);
+        }
+
+        [Test]
+        public void TestVectorsDigest256()
+        {
+            ImplTestVectorsDigest(SparkleDigest.SparkleParameters.ESCH256, "256");
+        }
+
+        [Test]
+        public void TestVectorsDigest384()
+        {
+            ImplTestVectorsDigest(SparkleDigest.SparkleParameters.ESCH384, "384");
+        }
+
+        private static void ImplBenchDigest(SparkleDigest.SparkleParameters sparkleParameters)
+        {
+            var digest = new SparkleDigest(sparkleParameters);
+
+            byte[] data = new byte[1024];
+            for (int i = 0; i < 1024; ++i)
+            {
+                for (int j = 0; j < 1024; ++j)
+                {
+#if NET6_0_OR_GREATER
+                    digest.BlockUpdate(data);
+#else
+                    digest.BlockUpdate(data, 0, 1024);
+#endif
+                }
+
+                digest.DoFinal(data, 0);
+            }
+        }
+
+        private static void ImplTestVectorsDigest(SparkleDigest.SparkleParameters sparkleParameters, string filename)
+        {
+            SparkleDigest sparkle = new SparkleDigest(sparkleParameters);
+            var map = new Dictionary<string, string>();
+            using (var src = new StreamReader(
+                SimpleTest.GetTestDataAsStream("crypto.sparkle.LWC_HASH_KAT_" + filename + ".txt")))
+            {
+                string line;
+                while ((line = src.ReadLine()) != null)
+                {
+                    int a = line.IndexOf('=');
+                    if (a < 0)
+                    {
+                        byte[] ptByte = Hex.Decode(map["Msg"]);
+                        byte[] expected = Hex.Decode(map["MD"]);
+                        map.Clear();
+
+                        sparkle.BlockUpdate(ptByte, 0, ptByte.Length);
+                        byte[] hash = new byte[sparkle.GetDigestSize()];
+                        sparkle.DoFinal(hash, 0);
+                        Assert.IsTrue(Arrays.AreEqual(expected, hash));
+                    }
+                    else
+                    {
+                        map[line.Substring(0, a).Trim()] = line.Substring(a + 1).Trim();
+                    }
+                }
+            }
+        }
+
+        private static void ImplTestExceptionsDigest(SparkleDigest.SparkleParameters sparkleParameters, int digestSize)
+        {
+            var sparkle = new SparkleDigest(sparkleParameters);
+
+            Assert.AreEqual(digestSize, sparkle.GetDigestSize(),
+                sparkle.AlgorithmName + ": GetDigestSize() is not correct");
+
+            try
+            {
+                sparkle.BlockUpdate(new byte[1], 1, 1);
+                Assert.Fail(sparkle.AlgorithmName + ": input for BlockUpdate is too short");
+            }
+            catch (DataLengthException)
+            {
+                //expected
+            }
+            try
+            {
+                sparkle.DoFinal(new byte[digestSize - 1], 2);
+                Assert.Fail(sparkle.AlgorithmName + ": output for Dofinal is too short");
+            }
+            catch (DataLengthException)
+            {
+                //expected
+            }
+        }
+    }
+}