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
+ }
+ }
+ }
+}
|