diff --git a/crypto/src/crypto/digests/SparkleDigest.cs b/crypto/src/crypto/digests/SparkleDigest.cs
index 3a87d9e80..df51c9935 100644
--- a/crypto/src/crypto/digests/SparkleDigest.cs
+++ b/crypto/src/crypto/digests/SparkleDigest.cs
@@ -1,15 +1,9 @@
using System;
-using System.Diagnostics;
#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
using System.Runtime.CompilerServices;
#endif
-#if NETCOREAPP3_0_OR_GREATER
-using System.Buffers.Binary;
-using System.Runtime.InteropServices;
-using System.Runtime.Intrinsics;
-using System.Runtime.Intrinsics.X86;
-#endif
+using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
@@ -188,19 +182,19 @@ namespace Org.BouncyCastle.Crypto.Digests
// addition of last msg block (incl. padding)
ProcessBlock(m_buf, 0, SPARKLE_STEPS_BIG);
+ Pack.UInt32_To_LE(state, 0, RATE_UINTS, output, outOff);
+
if (STATE_UINTS == 16)
{
- OutputBlock16(output, outOff);
- SparkleOpt16(state, SPARKLE_STEPS_SLIM);
- OutputBlock16(output, outOff + 16);
- SparkleOpt16(state, SPARKLE_STEPS_SLIM);
- OutputBlock16(output, outOff + 32);
+ SparkleEngine.SparkleOpt16(state, SPARKLE_STEPS_SLIM);
+ Pack.UInt32_To_LE(state, 0, RATE_UINTS, output, outOff + 16);
+ SparkleEngine.SparkleOpt16(state, SPARKLE_STEPS_SLIM);
+ Pack.UInt32_To_LE(state, 0, RATE_UINTS, output, outOff + 32);
}
else
{
- OutputBlock12(output, outOff);
- SparkleOpt12(state, SPARKLE_STEPS_SLIM);
- OutputBlock12(output, outOff + 16);
+ SparkleEngine.SparkleOpt12(state, SPARKLE_STEPS_SLIM);
+ Pack.UInt32_To_LE(state, 0, RATE_UINTS, output, outOff + 16);
}
Reset();
@@ -231,19 +225,19 @@ namespace Org.BouncyCastle.Crypto.Digests
// addition of last msg block (incl. padding)
ProcessBlock(m_buf, SPARKLE_STEPS_BIG);
+ Pack.UInt32_To_LE(state[..RATE_UINTS], output);
+
if (STATE_UINTS == 16)
{
- OutputBlock16(output);
- SparkleOpt16(state, SPARKLE_STEPS_SLIM);
- OutputBlock16(output[16..]);
- SparkleOpt16(state, SPARKLE_STEPS_SLIM);
- OutputBlock16(output[32..]);
+ SparkleEngine.SparkleOpt16(state, SPARKLE_STEPS_SLIM);
+ Pack.UInt32_To_LE(state[..RATE_UINTS], output[16..]);
+ SparkleEngine.SparkleOpt16(state, SPARKLE_STEPS_SLIM);
+ Pack.UInt32_To_LE(state[..RATE_UINTS], output[32..]);
}
else
{
- OutputBlock12(output);
- SparkleOpt12(state, SPARKLE_STEPS_SLIM);
- OutputBlock12(output[16..]);
+ SparkleEngine.SparkleOpt12(state, SPARKLE_STEPS_SLIM);
+ Pack.UInt32_To_LE(state[..RATE_UINTS], output[16..]);
}
Reset();
@@ -258,34 +252,6 @@ namespace Org.BouncyCastle.Crypto.Digests
m_bufPos = 0;
}
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
- private void OutputBlock12(Span<byte> output)
- {
- Pack.UInt32_To_LE(state[..RATE_UINTS], output);
- }
-
- private void OutputBlock16(Span<byte> output)
- {
- Pack.UInt32_To_LE(state[0], output);
- Pack.UInt32_To_LE(state[4], output[4..]);
- Pack.UInt32_To_LE(state[1], output[8..]);
- Pack.UInt32_To_LE(state[5], output[12..]);
- }
-#else
- private void OutputBlock12(byte[] output, int outOff)
- {
- Pack.UInt32_To_LE(state, 0, RATE_UINTS, output, outOff);
- }
-
- private void OutputBlock16(byte[] output, int outOff)
- {
- Pack.UInt32_To_LE(state[0], output, outOff);
- Pack.UInt32_To_LE(state[4], output, outOff + 4);
- Pack.UInt32_To_LE(state[1], output, outOff + 8);
- Pack.UInt32_To_LE(state[5], output, outOff + 12);
- }
-#endif
-
#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
@@ -308,278 +274,22 @@ namespace Org.BouncyCastle.Crypto.Digests
// addition of a buffer block to the state
uint tx = ELL(t0 ^ t2);
uint ty = ELL(t1 ^ t3);
+ state[0] ^= t0 ^ ty;
+ state[1] ^= t1 ^ tx;
+ state[2] ^= t2 ^ ty;
+ state[3] ^= t3 ^ tx;
+ state[4] ^= ty;
+ state[5] ^= tx;
if (STATE_UINTS == 16)
{
- state[0] ^= t0 ^ ty;
- state[1] ^= t2 ^ ty;
- state[2] ^= ty;
- state[3] ^= ty;
- state[4] ^= t1 ^ tx;
- state[5] ^= t3 ^ tx;
- state[6] ^= tx;
+ state[6] ^= ty;
state[7] ^= tx;
- SparkleOpt16(state, steps);
+ SparkleEngine.SparkleOpt16(state, steps);
}
else
{
- state[0] ^= t0 ^ ty;
- state[1] ^= t1 ^ tx;
- state[2] ^= t2 ^ ty;
- state[3] ^= t3 ^ tx;
- state[4] ^= ty;
- state[5] ^= tx;
- SparkleOpt12(state, steps);
- }
- }
-
- private static void SparkleOpt12(uint[] state, int steps)
- {
- uint s00 = state[ 0];
- uint s01 = state[ 1];
- uint s02 = state[ 2];
- uint s03 = state[ 3];
- uint s04 = state[ 4];
- uint s05 = state[ 5];
- uint s06 = state[ 6];
- uint s07 = state[ 7];
- uint s08 = state[ 8];
- uint s09 = state[ 9];
- uint s10 = state[10];
- uint s11 = state[11];
-
- for (int i = 0; i < steps; ++i)
- {
- // Add round ant
-
- s01 ^= RCON[i & 7];
- s03 ^= (uint)i;
-
- // ARXBOX layer
-
- ArxBoxRound(RCON[0], ref s00, ref s01);
- ArxBoxRound(RCON[1], ref s02, ref s03);
- ArxBoxRound(RCON[2], ref s04, ref s05);
- ArxBoxRound(RCON[3], ref s06, ref s07);
- ArxBoxRound(RCON[4], ref s08, ref s09);
- ArxBoxRound(RCON[5], ref s10, ref s11);
-
- // Linear layer
-
- uint t024 = ELL(s00 ^ s02 ^ s04);
- uint t135 = ELL(s01 ^ s03 ^ s05);
-
- uint u00 = s00 ^ s06;
- uint u01 = s01 ^ s07;
- uint u02 = s02 ^ s08;
- uint u03 = s03 ^ s09;
- uint u04 = s04 ^ s10;
- uint u05 = s05 ^ s11;
-
- s06 = s00;
- s07 = s01;
- s08 = s02;
- s09 = s03;
- s10 = s04;
- s11 = s05;
-
- s00 = u02 ^ t135;
- s01 = u03 ^ t024;
- s02 = u04 ^ t135;
- s03 = u05 ^ t024;
- s04 = u00 ^ t135;
- s05 = u01 ^ t024;
+ SparkleEngine.SparkleOpt12(state, steps);
}
-
- state[ 0] = s00;
- state[ 1] = s01;
- state[ 2] = s02;
- state[ 3] = s03;
- state[ 4] = s04;
- state[ 5] = s05;
- state[ 6] = s06;
- state[ 7] = s07;
- state[ 8] = s08;
- state[ 9] = s09;
- state[10] = s10;
- state[11] = s11;
- }
-
- private static void SparkleOpt16(uint[] state, int steps)
- {
- Debug.Assert((steps & 1) == 0);
-
-#if NETCOREAPP3_0_OR_GREATER
- if (Sse2.IsSupported)
- {
- var s0246 = Load128(state.AsSpan(0));
- var s1357 = Load128(state.AsSpan(4));
- var s8ACE = Load128(state.AsSpan(8));
- var s9BDF = Load128(state.AsSpan(12));
-
- var RC03 = Load128(RCON.AsSpan(0));
- var RC47 = Load128(RCON.AsSpan(4));
-
- for (int step = 0; step < steps; ++step)
- {
- // Add round ant
-
- s1357 = Sse2.Xor(s1357, Vector128.Create(RCON[step & 7], (uint)step, 0U, 0U));
-
- // ARXBOX layer
-
- ArxBoxRound(RC03, ref s0246, ref s1357);
- ArxBoxRound(RC47, ref s8ACE, ref s9BDF);
-
- // Linear layer
-
- var t0246 = ELL(HorizontalXor(s0246));
- var t1357 = ELL(HorizontalXor(s1357));
-
- var u0246 = Sse2.Xor(s0246, s8ACE);
- var u1357 = Sse2.Xor(s1357, s9BDF);
-
- s8ACE = s0246;
- s9BDF = s1357;
-
- s0246 = Sse2.Xor(t1357, Sse2.Shuffle(u0246, 0x39));
- s1357 = Sse2.Xor(t0246, Sse2.Shuffle(u1357, 0x39));
- }
-
- Store128(s0246, state.AsSpan(0));
- Store128(s1357, state.AsSpan(4));
- Store128(s8ACE, state.AsSpan(8));
- Store128(s9BDF, state.AsSpan(12));
- }
- else
-#endif
- {
- uint s00 = state[ 0];
- uint s02 = state[ 1];
- uint s04 = state[ 2];
- uint s06 = state[ 3];
- uint s01 = state[ 4];
- uint s03 = state[ 5];
- uint s05 = state[ 6];
- uint s07 = state[ 7];
- uint s08 = state[ 8];
- uint s10 = state[ 9];
- uint s12 = state[10];
- uint s14 = state[11];
- uint s09 = state[12];
- uint s11 = state[13];
- uint s13 = state[14];
- uint s15 = state[15];
-
- int step = 0;
- while (step < steps)
- {
- // STEP 1
-
- // Add round ant
-
- s01 ^= RCON[step & 7];
- s03 ^= (uint)(step++);
-
- // ARXBOX layer
-
- ArxBoxRound(RCON[0], ref s00, ref s01);
- ArxBoxRound(RCON[1], ref s02, ref s03);
- ArxBoxRound(RCON[2], ref s04, ref s05);
- ArxBoxRound(RCON[3], ref s06, ref s07);
- ArxBoxRound(RCON[4], ref s08, ref s09);
- ArxBoxRound(RCON[5], ref s10, ref s11);
- ArxBoxRound(RCON[6], ref s12, ref s13);
- ArxBoxRound(RCON[7], ref s14, ref s15);
-
- // Linear layer
-
- uint t0246 = ELL(s00 ^ s02 ^ s04 ^ s06);
- uint t1357 = ELL(s01 ^ s03 ^ s05 ^ s07);
-
- uint u08 = s08;
- uint u09 = s09;
-
- s08 = s02 ^ s10 ^ t1357;
- s09 = s03 ^ s11 ^ t0246;
- s10 = s04 ^ s12 ^ t1357;
- s11 = s05 ^ s13 ^ t0246;
- s12 = s06 ^ s14 ^ t1357;
- s13 = s07 ^ s15 ^ t0246;
- s14 = s00 ^ u08 ^ t1357;
- s15 = s01 ^ u09 ^ t0246;
-
- // STEP 2
-
- // Add round ant
-
- s09 ^= RCON[step & 7];
- s11 ^= (uint)(step++);
-
- // ARXBOX layer
-
- ArxBoxRound(RCON[0], ref s08, ref s09);
- ArxBoxRound(RCON[1], ref s10, ref s11);
- ArxBoxRound(RCON[2], ref s12, ref s13);
- ArxBoxRound(RCON[3], ref s14, ref s15);
- ArxBoxRound(RCON[4], ref s00, ref s01);
- ArxBoxRound(RCON[5], ref s02, ref s03);
- ArxBoxRound(RCON[6], ref s04, ref s05);
- ArxBoxRound(RCON[7], ref s06, ref s07);
-
- // Linear layer
-
- uint t8ACE = ELL(s08 ^ s10 ^ s12 ^ s14);
- uint t9BDF = ELL(s09 ^ s11 ^ s13 ^ s15);
-
- uint u00 = s00;
- uint u01 = s01;
-
- s00 = s02 ^ s10 ^ t9BDF;
- s01 = s03 ^ s11 ^ t8ACE;
- s02 = s04 ^ s12 ^ t9BDF;
- s03 = s05 ^ s13 ^ t8ACE;
- s04 = s06 ^ s14 ^ t9BDF;
- s05 = s07 ^ s15 ^ t8ACE;
- s06 = u00 ^ s08 ^ t9BDF;
- s07 = u01 ^ s09 ^ t8ACE;
- }
-
- state[ 0] = s00;
- state[ 1] = s02;
- state[ 2] = s04;
- state[ 3] = s06;
- state[ 4] = s01;
- state[ 5] = s03;
- state[ 6] = s05;
- state[ 7] = s07;
- state[ 8] = s08;
- state[ 9] = s10;
- state[10] = s12;
- state[11] = s14;
- state[12] = s09;
- state[13] = s11;
- state[14] = s13;
- state[15] = s15;
- }
- }
-
-#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
-#endif
- private static void ArxBoxRound(uint rc, ref uint s00, ref uint s01)
- {
- s00 += Integers.RotateRight(s01, 31);
- s01 ^= Integers.RotateRight(s00, 24);
- s00 ^= rc;
- s00 += Integers.RotateRight(s01, 17);
- s01 ^= Integers.RotateRight(s00, 17);
- s00 ^= rc;
- s00 += s01;
- s01 ^= Integers.RotateRight(s00, 31);
- s00 ^= rc;
- s00 += Integers.RotateRight(s01, 24);
- s01 ^= Integers.RotateRight(s00, 16);
- s00 ^= rc;
}
#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
@@ -589,81 +299,5 @@ namespace Org.BouncyCastle.Crypto.Digests
{
return Integers.RotateRight(x, 16) ^ (x & 0xFFFFU);
}
-
-#if NETCOREAPP3_0_OR_GREATER
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void ArxBoxRound(Vector128<uint> rc, ref Vector128<uint> s00, ref Vector128<uint> s01)
- {
- s00 = Sse2.Add(s00, Sse2.ShiftRightLogical(s01, 31));
- s00 = Sse2.Add(s00, Sse2.ShiftLeftLogical(s01, 1));
-
- s01 = Sse2.Xor(s01, Sse2.ShiftRightLogical(s00, 24));
- s01 = Sse2.Xor(s01, Sse2.ShiftLeftLogical(s00, 8));
-
- s00 = Sse2.Xor(s00, rc);
-
- s00 = Sse2.Add(s00, Sse2.ShiftRightLogical(s01, 17));
- s00 = Sse2.Add(s00, Sse2.ShiftLeftLogical(s01, 15));
-
- s01 = Sse2.Xor(s01, Sse2.ShiftRightLogical(s00, 17));
- s01 = Sse2.Xor(s01, Sse2.ShiftLeftLogical(s00, 15));
-
- s00 = Sse2.Xor(s00, rc);
-
- s00 = Sse2.Add(s00, s01);
-
- s01 = Sse2.Xor(s01, Sse2.ShiftRightLogical(s00, 31));
- s01 = Sse2.Xor(s01, Sse2.ShiftLeftLogical(s00, 1));
-
- s00 = Sse2.Xor(s00, rc);
-
- s00 = Sse2.Add(s00, Sse2.ShiftRightLogical(s01, 24));
- s00 = Sse2.Add(s00, Sse2.ShiftLeftLogical(s01, 8));
-
- s01 = Sse2.Xor(s01, Sse2.ShiftRightLogical(s00, 16));
- s01 = Sse2.Xor(s01, Sse2.ShiftLeftLogical(s00, 16));
-
- s00 = Sse2.Xor(s00, rc);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static Vector128<uint> ELL(Vector128<uint> x)
- {
- var t = Sse2.ShiftLeftLogical(x, 16);
- var u = Sse2.Xor(x, t);
- return Sse2.Xor(t, Sse2.ShiftRightLogical(u, 16));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static Vector128<uint> HorizontalXor(Vector128<uint> x)
- {
- var t = Sse2.Xor(x, Sse2.Shuffle(x, 0x1B));
- return Sse2.Xor(t, Sse2.Shuffle(t, 0xB1));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static Vector128<uint> Load128(ReadOnlySpan<uint> t)
- {
- if (BitConverter.IsLittleEndian && Unsafe.SizeOf<Vector128<uint>>() == 16)
- return MemoryMarshal.Read<Vector128<uint>>(MemoryMarshal.AsBytes(t));
-
- return Vector128.Create(t[0], t[1], t[2], t[3]);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void Store128(Vector128<uint> s, Span<uint> t)
- {
- var b = MemoryMarshal.AsBytes(t);
- if (BitConverter.IsLittleEndian && Unsafe.SizeOf<Vector128<uint>>() == 16)
- {
- MemoryMarshal.Write(b, ref s);
- return;
- }
-
- var u = s.AsUInt64();
- BinaryPrimitives.WriteUInt64LittleEndian(b[..8], u.GetElement(0));
- BinaryPrimitives.WriteUInt64LittleEndian(b[8..], u.GetElement(1));
- }
-#endif
}
}
diff --git a/crypto/src/crypto/engines/SparkleEngine.cs b/crypto/src/crypto/engines/SparkleEngine.cs
new file mode 100644
index 000000000..34c9bf015
--- /dev/null
+++ b/crypto/src/crypto/engines/SparkleEngine.cs
@@ -0,0 +1,1394 @@
+using System;
+using System.Diagnostics;
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+using System.Runtime.CompilerServices;
+#endif
+#if NETCOREAPP3_0_OR_GREATER
+using System.Buffers.Binary;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+#endif
+
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Engines
+{
+ /// <summary>Sparkle v1.2, based on the current round 3 submission, https://sparkle-lwc.github.io/ .</summary>
+ /// <remarks>
+ /// Reference C implementation: https://github.com/cryptolu/sparkle.<br/>
+ /// Specification:
+ /// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/sparkle-spec-final.pdf .
+ /// </remarks>
+ public sealed class SparkleEngine
+ : IAeadCipher
+ {
+ public enum SparkleParameters
+ {
+ SCHWAEMM128_128,
+ SCHWAEMM256_128,
+ SCHWAEMM192_192,
+ SCHWAEMM256_256
+ }
+
+ private enum State
+ {
+ Uninitialized = 0,
+ EncInit = 1,
+ EncAad = 2,
+ EncData = 3,
+ EncFinal = 4,
+ DecInit = 5,
+ DecAad = 6,
+ DecData = 7,
+ DecFinal = 8,
+ }
+
+ private static readonly uint[] RCON = { 0xB7E15162U, 0xBF715880U, 0x38B4DA56U, 0x324E7738U, 0xBB1185EBU,
+ 0x4F7C7B57U, 0xCFBFA1C8U, 0xC2B3293DU };
+
+ private string algorithmName;
+ private readonly uint[] state;
+ private readonly uint[] k;
+ private readonly uint[] npub;
+ private byte[] tag;
+ private bool encrypted;
+ private State m_state = State.Uninitialized;
+ private byte[] initialAssociatedText;
+
+ private readonly int m_bufferSizeDecrypt;
+ private readonly byte[] m_buf;
+ private int m_bufPos = 0;
+
+ private readonly int SCHWAEMM_KEY_LEN;
+ private readonly int SCHWAEMM_NONCE_LEN;
+ private readonly int SPARKLE_STEPS_SLIM;
+ private readonly int SPARKLE_STEPS_BIG;
+ private readonly int KEY_BYTES;
+ private readonly int KEY_WORDS;
+ private readonly int TAG_WORDS;
+ private readonly int TAG_BYTES;
+ private readonly int STATE_WORDS;
+ private readonly int RATE_WORDS;
+ private readonly int RATE_BYTES;
+ private readonly int CAP_MASK;
+ private readonly uint _A0;
+ private readonly uint _A1;
+ private readonly uint _M2;
+ private readonly uint _M3;
+
+ public SparkleEngine(SparkleParameters sparkleParameters)
+ {
+ int SPARKLE_STATE;
+ int SCHWAEMM_TAG_LEN;
+ int SPARKLE_CAPACITY;
+ switch (sparkleParameters)
+ {
+ case SparkleParameters.SCHWAEMM128_128:
+ SCHWAEMM_KEY_LEN = 128;
+ SCHWAEMM_NONCE_LEN = 128;
+ SCHWAEMM_TAG_LEN = 128;
+ SPARKLE_STATE = 256;
+ SPARKLE_CAPACITY = 128;
+ SPARKLE_STEPS_SLIM = 7;
+ SPARKLE_STEPS_BIG = 10;
+ algorithmName = "SCHWAEMM128-128";
+ break;
+ case SparkleParameters.SCHWAEMM256_128:
+ SCHWAEMM_KEY_LEN = 128;
+ SCHWAEMM_NONCE_LEN = 256;
+ SCHWAEMM_TAG_LEN = 128;
+ SPARKLE_STATE = 384;
+ SPARKLE_CAPACITY = 128;
+ SPARKLE_STEPS_SLIM = 7;
+ SPARKLE_STEPS_BIG = 11;
+ algorithmName = "SCHWAEMM256-128";
+ break;
+ case SparkleParameters.SCHWAEMM192_192:
+ SCHWAEMM_KEY_LEN = 192;
+ SCHWAEMM_NONCE_LEN = 192;
+ SCHWAEMM_TAG_LEN = 192;
+ SPARKLE_STATE = 384;
+ SPARKLE_CAPACITY = 192;
+ SPARKLE_STEPS_SLIM = 7;
+ SPARKLE_STEPS_BIG = 11;
+ algorithmName = "SCHWAEMM192-192";
+ break;
+ case SparkleParameters.SCHWAEMM256_256:
+ SCHWAEMM_KEY_LEN = 256;
+ SCHWAEMM_NONCE_LEN = 256;
+ SCHWAEMM_TAG_LEN = 256;
+ SPARKLE_STATE = 512;
+ SPARKLE_CAPACITY = 256;
+ SPARKLE_STEPS_SLIM = 8;
+ SPARKLE_STEPS_BIG = 12;
+ algorithmName = "SCHWAEMM256-256";
+ break;
+ default:
+ throw new ArgumentException("Invalid definition of SCHWAEMM instance");
+ }
+ KEY_WORDS = SCHWAEMM_KEY_LEN >> 5;
+ KEY_BYTES = SCHWAEMM_KEY_LEN >> 3;
+ TAG_WORDS = SCHWAEMM_TAG_LEN >> 5;
+ TAG_BYTES = SCHWAEMM_TAG_LEN >> 3;
+ STATE_WORDS = SPARKLE_STATE >> 5;
+ RATE_WORDS = SCHWAEMM_NONCE_LEN >> 5;
+ RATE_BYTES = SCHWAEMM_NONCE_LEN >> 3;
+ int CAP_BRANS = SPARKLE_CAPACITY >> 6;
+ int CAP_WORDS = SPARKLE_CAPACITY >> 5;
+ CAP_MASK = RATE_WORDS > CAP_WORDS ? CAP_WORDS - 1 : -1;
+ _A0 = ((((1u << CAP_BRANS))) << 24);
+ _A1 = (((1u ^ (1u << CAP_BRANS))) << 24);
+ _M2 = (((2u ^ (1u << CAP_BRANS))) << 24);
+ _M3 = (((3u ^ (1u << CAP_BRANS))) << 24);
+ state = new uint[STATE_WORDS];
+ tag = new byte[TAG_BYTES];
+ k = new uint[KEY_WORDS];
+ npub = new uint[RATE_WORDS];
+
+ m_bufferSizeDecrypt = RATE_BYTES + TAG_BYTES;
+ m_buf = new byte[m_bufferSizeDecrypt];
+
+ // Relied on by ProcessBytes methods for decryption
+ Debug.Assert(RATE_BYTES >= TAG_BYTES);
+ }
+
+ public int GetKeyBytesSize() => KEY_BYTES;
+
+ public int GetIVBytesSize() => RATE_BYTES;
+
+ public string AlgorithmName => algorithmName;
+
+ public void Init(bool forEncryption, ICipherParameters parameters)
+ {
+ KeyParameter key;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ ReadOnlySpan<byte> iv;
+#else
+ byte[] iv;
+#endif
+
+ if (parameters is AeadParameters aeadParameters)
+ {
+ key = aeadParameters.Key;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ iv = aeadParameters.Nonce;
+#else
+ iv = aeadParameters.GetNonce();
+#endif
+ initialAssociatedText = aeadParameters.GetAssociatedText();
+
+ int macSizeBits = aeadParameters.MacSize;
+ if (macSizeBits != TAG_BYTES * 8)
+ throw new ArgumentException("Invalid value for MAC size: " + macSizeBits);
+ }
+ else if (parameters is ParametersWithIV withIV)
+ {
+ key = withIV.Parameters as KeyParameter;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ iv = withIV.IV;
+#else
+ iv = withIV.GetIV();
+#endif
+ initialAssociatedText = null;
+ }
+ else
+ {
+ throw new ArgumentException("invalid parameters passed to Sparkle");
+ }
+
+ if (key == null)
+ throw new ArgumentException("Sparkle Init parameters must include a key");
+
+ int expectedKeyLength = KEY_WORDS * 4;
+ if (expectedKeyLength != key.KeyLength)
+ throw new ArgumentException(algorithmName + " requires exactly " + expectedKeyLength + " bytes of key");
+
+ int expectedIVLength = RATE_WORDS * 4;
+ if (expectedIVLength != iv.Length)
+ throw new ArgumentException(algorithmName + " requires exactly " + expectedIVLength + " bytes of IV");
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ Pack.LE_To_UInt32(key.Key, k);
+ Pack.LE_To_UInt32(iv, npub);
+#else
+ Pack.LE_To_UInt32(key.GetKey(), 0, k);
+ Pack.LE_To_UInt32(iv, 0, npub);
+#endif
+
+ m_state = forEncryption ? State.EncInit : State.DecInit;
+
+ // TODO true might be better?
+ Reset(false);
+ }
+
+ public void ProcessAadByte(byte input)
+ {
+ CheckAad();
+
+ if (m_bufPos == RATE_BYTES)
+ {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ ProcessBufferAad(m_buf);
+#else
+ ProcessBufferAad(m_buf, 0);
+#endif
+ m_bufPos = 0;
+ }
+
+ m_buf[m_bufPos++] = input;
+ }
+
+ public void ProcessAadBytes(byte[] inBytes, int inOff, int len)
+ {
+ Check.DataLength(inBytes, inOff, len, "input buffer too short");
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ ProcessAadBytes(inBytes.AsSpan(inOff, len));
+#else
+ // Don't enter AAD state until we actually get input
+ if (len <= 0)
+ return;
+
+ CheckAad();
+
+ if (m_bufPos > 0)
+ {
+ int available = RATE_BYTES - m_bufPos;
+ if (len <= available)
+ {
+ Array.Copy(inBytes, inOff, m_buf, m_bufPos, len);
+ m_bufPos += len;
+ return;
+ }
+
+ Array.Copy(inBytes, inOff, m_buf, m_bufPos, available);
+ inOff += available;
+ len -= available;
+
+ ProcessBufferAad(m_buf, 0);
+ //m_bufPos = 0;
+ }
+
+ while (len > RATE_BYTES)
+ {
+ ProcessBufferAad(inBytes, inOff);
+ inOff += RATE_BYTES;
+ len -= RATE_BYTES;
+ }
+
+ Array.Copy(inBytes, inOff, m_buf, 0, len);
+ m_bufPos = len;
+#endif
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public void ProcessAadBytes(ReadOnlySpan<byte> input)
+ {
+ // Don't enter AAD state until we actually get input
+ if (input.IsEmpty)
+ return;
+
+ CheckAad();
+
+ if (m_bufPos > 0)
+ {
+ int available = RATE_BYTES - m_bufPos;
+ if (input.Length <= available)
+ {
+ input.CopyTo(m_buf.AsSpan(m_bufPos));
+ m_bufPos += input.Length;
+ return;
+ }
+
+ input[..available].CopyTo(m_buf.AsSpan(m_bufPos));
+ input = input[available..];
+
+ ProcessBufferAad(m_buf);
+ //m_bufPos = 0;
+ }
+
+ while (input.Length > RATE_BYTES)
+ {
+ ProcessBufferAad(input);
+ input = input[RATE_BYTES..];
+ }
+
+ input.CopyTo(m_buf);
+ m_bufPos = input.Length;
+ }
+#endif
+
+ public int ProcessByte(byte input, byte[] outBytes, int outOff)
+ {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ return ProcessByte(input, Spans.FromNullable(outBytes, outOff));
+#else
+ return ProcessBytes(new byte[]{ input }, 0, 1, outBytes, outOff);
+#endif
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public int ProcessByte(byte input, Span<byte> output)
+ {
+ Span<byte> singleByte = stackalloc byte[1]{ input };
+
+ return ProcessBytes(singleByte, output);
+ }
+#endif
+
+ public int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff)
+ {
+ Check.DataLength(inBytes, inOff, len, "input buffer too short");
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ return ProcessBytes(inBytes.AsSpan(inOff, len), Spans.FromNullable(outBytes, outOff));
+#else
+ bool forEncryption = CheckData();
+
+ int resultLength = 0;
+
+ if (forEncryption)
+ {
+ if (m_bufPos > 0)
+ {
+ int available = RATE_BYTES - m_bufPos;
+ if (len <= available)
+ {
+ Array.Copy(inBytes, inOff, m_buf, m_bufPos, len);
+ m_bufPos += len;
+ return 0;
+ }
+
+ Array.Copy(inBytes, inOff, m_buf, m_bufPos, available);
+ inOff += available;
+ len -= available;
+
+ ProcessBufferEncrypt(m_buf, 0, outBytes, outOff);
+ resultLength = RATE_BYTES;
+ //m_bufPos = 0;
+ }
+
+ while (len > RATE_BYTES)
+ {
+ ProcessBufferEncrypt(inBytes, inOff, outBytes, outOff + resultLength);
+ inOff += RATE_BYTES;
+ len -= RATE_BYTES;
+ resultLength += RATE_BYTES;
+ }
+ }
+ else
+ {
+ int available = m_bufferSizeDecrypt - m_bufPos;
+ if (len <= available)
+ {
+ Array.Copy(inBytes, inOff, m_buf, m_bufPos, len);
+ m_bufPos += len;
+ return 0;
+ }
+
+ if (m_bufPos > RATE_BYTES)
+ {
+ ProcessBufferDecrypt(m_buf, 0, outBytes, outOff);
+ m_bufPos -= RATE_BYTES;
+ Array.Copy(m_buf, RATE_BYTES, m_buf, 0, m_bufPos);
+ resultLength = RATE_BYTES;
+
+ available += RATE_BYTES;
+ if (len <= available)
+ {
+ Array.Copy(inBytes, inOff, m_buf, m_bufPos, len);
+ m_bufPos += len;
+ return resultLength;
+ }
+ }
+
+ available = RATE_BYTES - m_bufPos;
+ Array.Copy(inBytes, inOff, m_buf, m_bufPos, available);
+ inOff += available;
+ len -= available;
+ ProcessBufferDecrypt(m_buf, 0, outBytes, outOff + resultLength);
+ resultLength += RATE_BYTES;
+ //m_bufPos = 0;
+
+ while (len > m_bufferSizeDecrypt)
+ {
+ ProcessBufferDecrypt(inBytes, inOff, outBytes, outOff + resultLength);
+ inOff += RATE_BYTES;
+ len -= RATE_BYTES;
+ resultLength += RATE_BYTES;
+ }
+ }
+
+ Array.Copy(inBytes, inOff, m_buf, 0, len);
+ m_bufPos = len;
+
+ return resultLength;
+#endif
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
+ {
+ bool forEncryption = CheckData();
+
+ int resultLength = 0;
+
+ if (forEncryption)
+ {
+ if (m_bufPos > 0)
+ {
+ int available = RATE_BYTES - m_bufPos;
+ if (input.Length <= available)
+ {
+ input.CopyTo(m_buf.AsSpan(m_bufPos));
+ m_bufPos += input.Length;
+ return 0;
+ }
+
+ input[..available].CopyTo(m_buf.AsSpan(m_bufPos));
+ input = input[available..];
+
+ ProcessBufferEncrypt(m_buf, output);
+ resultLength = RATE_BYTES;
+ //m_bufPos = 0;
+ }
+
+ while (input.Length > RATE_BYTES)
+ {
+ ProcessBufferEncrypt(input, output[resultLength..]);
+ input = input[RATE_BYTES..];
+ resultLength += RATE_BYTES;
+ }
+ }
+ else
+ {
+ int available = m_bufferSizeDecrypt - m_bufPos;
+ if (input.Length <= available)
+ {
+ input.CopyTo(m_buf.AsSpan(m_bufPos));
+ m_bufPos += input.Length;
+ return 0;
+ }
+
+ if (m_bufPos > RATE_BYTES)
+ {
+ ProcessBufferDecrypt(m_buf, output);
+ m_bufPos -= RATE_BYTES;
+ m_buf.AsSpan(0, m_bufPos).CopyFrom(m_buf.AsSpan(RATE_BYTES));
+ resultLength = RATE_BYTES;
+
+ available += RATE_BYTES;
+ if (input.Length <= available)
+ {
+ input.CopyTo(m_buf.AsSpan(m_bufPos));
+ m_bufPos += input.Length;
+ return resultLength;
+ }
+ }
+
+ available = RATE_BYTES - m_bufPos;
+ input[..available].CopyTo(m_buf.AsSpan(m_bufPos));
+ input = input[available..];
+ ProcessBufferDecrypt(m_buf, output[resultLength..]);
+ resultLength += RATE_BYTES;
+ //m_bufPos = 0;
+
+ while (input.Length > m_bufferSizeDecrypt)
+ {
+ ProcessBufferDecrypt(input, output[resultLength..]);
+ input = input[RATE_BYTES..];
+ resultLength += RATE_BYTES;
+ }
+ }
+
+ input.CopyTo(m_buf);
+ m_bufPos = input.Length;
+
+ return resultLength;
+ }
+#endif
+
+ public int DoFinal(byte[] outBytes, int outOff)
+ {
+ bool forEncryption = CheckData();
+
+ int resultLength;
+ if (forEncryption)
+ {
+ resultLength = m_bufPos + TAG_BYTES;
+ Check.OutputLength(outBytes, outOff, resultLength, "output buffer too short");
+
+ }
+ else
+ {
+ if (m_bufPos < TAG_BYTES)
+ throw new InvalidCipherTextException("data too short");
+
+ m_bufPos -= TAG_BYTES;
+
+ resultLength = m_bufPos;
+ Check.OutputLength(outBytes, outOff, resultLength, "output buffer too short");
+
+ }
+
+ if (encrypted || m_bufPos > 0)
+ {
+ // Encryption of Last Block
+ // addition of ant M2 or M3 to the state
+ state[STATE_WORDS - 1] ^= (m_bufPos < RATE_BYTES) ? _M2 : _M3;
+ // combined Rho and rate-whitening (incl. padding)
+ // Rho and rate-whitening for the encryption of the last plaintext block. Since
+ // this last block may require padding, it is always copied to a buffer.
+ uint[] buffer = new uint[RATE_WORDS];
+ for (int i = 0; i < m_bufPos; ++i)
+ {
+ buffer[i >> 2] |= (uint)m_buf[i] << ((i & 3) << 3);
+ }
+ if (m_bufPos < RATE_BYTES)
+ {
+ if (!forEncryption)
+ {
+ int tmp = (m_bufPos & 3) << 3;
+ buffer[m_bufPos >> 2] |= (state[m_bufPos >> 2] >> tmp) << tmp;
+ tmp = (m_bufPos >> 2) + 1;
+ Array.Copy(state, tmp, buffer, tmp, RATE_WORDS - tmp);
+ }
+ buffer[m_bufPos >> 2] ^= 0x80U << ((m_bufPos & 3) << 3);
+ }
+ for (int i = 0; i < RATE_WORDS / 2; ++i)
+ {
+ int j = i + RATE_WORDS / 2;
+
+ uint s_i = state[i];
+ uint s_j = state[j];
+ if (forEncryption)
+ {
+ state[i] = s_j ^ buffer[i] ^ state[RATE_WORDS + i];
+ state[j] = s_i ^ s_j ^ buffer[j] ^ state[RATE_WORDS + (j & CAP_MASK)];
+ }
+ else
+ {
+ state[i] = s_i ^ s_j ^ buffer[i] ^ state[RATE_WORDS + i];
+ state[j] = s_i ^ buffer[j] ^ state[RATE_WORDS + (j & CAP_MASK)];
+ }
+ buffer[i] ^= s_i;
+ buffer[j] ^= s_j;
+ }
+ for (int i = 0; i < m_bufPos; ++i)
+ {
+ outBytes[outOff++] = (byte)(buffer[i >> 2] >> ((i & 3) << 3));
+ }
+
+ SparkleOpt(state, SPARKLE_STEPS_BIG);
+ }
+ // add key to the capacity-part of the state
+ for (int i = 0; i < KEY_WORDS; i++)
+ {
+ state[RATE_WORDS + i] ^= k[i];
+ }
+ tag = new byte[TAG_BYTES];
+ Pack.UInt32_To_LE(state, RATE_WORDS, TAG_WORDS, tag, 0);
+ if (forEncryption)
+ {
+ Array.Copy(tag, 0, outBytes, outOff, TAG_BYTES);
+ }
+ else
+ {
+ if (!Arrays.FixedTimeEquals(TAG_BYTES, tag, 0, m_buf, m_bufPos))
+ throw new InvalidCipherTextException("mac check in " + AlgorithmName + " failed");
+ }
+ Reset(!forEncryption);
+ return resultLength;
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public int DoFinal(Span<byte> output)
+ {
+ byte[] rv = new byte[GetOutputSize(0)];
+ int len = DoFinal(rv, 0);
+ rv.AsSpan(0, len).CopyTo(output);
+ return len;
+ }
+#endif
+
+ public byte[] GetMac()
+ {
+ return tag;
+ }
+
+ public int GetUpdateOutputSize(int len)
+ {
+ // The -1 is to account for the lazy processing of a full buffer
+ int total = System.Math.Max(0, len - 1);
+
+ switch (m_state)
+ {
+ case State.DecInit:
+ case State.DecAad:
+ total = System.Math.Max(0, total - TAG_BYTES);
+ break;
+ case State.DecData:
+ case State.DecFinal:
+ total = System.Math.Max(0, total + m_bufPos - TAG_BYTES);
+ break;
+ case State.EncData:
+ case State.EncFinal:
+ total = System.Math.Max(0, total + m_bufPos);
+ break;
+ default:
+ break;
+ }
+
+ return total - total % RATE_BYTES;
+ }
+
+ public int GetOutputSize(int len)
+ {
+ int total = System.Math.Max(0, len);
+
+ switch (m_state)
+ {
+ case State.DecInit:
+ case State.DecAad:
+ return System.Math.Max(0, total - TAG_BYTES);
+ case State.DecData:
+ case State.DecFinal:
+ return System.Math.Max(0, total + m_bufPos - TAG_BYTES);
+ case State.EncData:
+ case State.EncFinal:
+ return total + m_bufPos + TAG_BYTES;
+ default:
+ return total + TAG_BYTES;
+ }
+ }
+
+ public void Reset()
+ {
+ Reset(true);
+ }
+
+ private void CheckAad()
+ {
+ switch (m_state)
+ {
+ case State.DecInit:
+ m_state = State.DecAad;
+ break;
+ case State.EncInit:
+ m_state = State.EncAad;
+ break;
+ case State.DecAad:
+ case State.EncAad:
+ break;
+ case State.EncFinal:
+ throw new InvalidOperationException(AlgorithmName + " cannot be reused for encryption");
+ default:
+ throw new InvalidOperationException(AlgorithmName + " needs to be initialized");
+ }
+ }
+
+ private bool CheckData()
+ {
+ switch (m_state)
+ {
+ case State.DecInit:
+ case State.DecAad:
+ FinishAad(State.DecData);
+ return false;
+ case State.EncInit:
+ case State.EncAad:
+ FinishAad(State.EncData);
+ return true;
+ case State.DecData:
+ return false;
+ case State.EncData:
+ return true;
+ case State.EncFinal:
+ throw new InvalidOperationException(AlgorithmName + " cannot be reused for encryption");
+ default:
+ throw new InvalidOperationException(AlgorithmName + " needs to be initialized");
+ }
+ }
+
+ private void FinishAad(State nextState)
+ {
+ // State indicates whether we ever received AAD
+ switch (m_state)
+ {
+ case State.DecAad:
+ case State.EncAad:
+ {
+ ProcessFinalAad();
+ break;
+ }
+ }
+
+ m_bufPos = 0;
+ m_state = nextState;
+ }
+
+ private void FinishData(State nextState)
+ {
+ // TODO
+ //switch (asconParameters)
+ //{
+ //case AsconParameters.ascon128:
+ // x1 ^= K1;
+ // x2 ^= K2;
+ // break;
+ //case AsconParameters.ascon128a:
+ // x2 ^= K1;
+ // x3 ^= K2;
+ // break;
+ //case AsconParameters.ascon80pq:
+ // x1 ^= (K0 << 32 | K1 >> 32);
+ // x2 ^= (K1 << 32 | K2 >> 32);
+ // x3 ^= K2 << 32;
+ // break;
+ //default:
+ // throw new InvalidOperationException();
+ //}
+ //P(12);
+ //x3 ^= K1;
+ //x4 ^= K2;
+
+ m_state = nextState;
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private void ProcessBufferAad(ReadOnlySpan<byte> buffer)
+ {
+ for (int i = 0; i < RATE_WORDS / 2; ++i)
+ {
+ int j = i + (RATE_WORDS >> 1);
+
+ uint si = state[i];
+ uint sj = state[j];
+
+ uint d_i = Pack.LE_To_UInt32(buffer, i << 2);
+ uint d_j = Pack.LE_To_UInt32(buffer, j << 2);
+
+ state[i] = sj ^ d_i ^ state[RATE_WORDS + i];
+ state[j] = si ^ sj ^ d_j ^ state[RATE_WORDS + (j & CAP_MASK)];
+ }
+
+ SparkleOpt(state, SPARKLE_STEPS_SLIM);
+ }
+
+ private void ProcessBufferDecrypt(ReadOnlySpan<byte> buffer, Span<byte> output)
+ {
+ Debug.Assert(buffer.Length >= RATE_BYTES);
+
+ Check.OutputLength(output, RATE_BYTES, "output buffer too short");
+
+ for (int i = 0; i < RATE_WORDS / 2; ++i)
+ {
+ int j = i + (RATE_WORDS / 2);
+
+ uint s_i = state[i];
+ uint s_j = state[j];
+
+ uint d_i = Pack.LE_To_UInt32(buffer, i * 4);
+ uint d_j = Pack.LE_To_UInt32(buffer, j * 4);
+
+ state[i] = s_i ^ s_j ^ d_i ^ state[RATE_WORDS + i];
+ state[j] = s_i ^ d_j ^ state[RATE_WORDS + (j & CAP_MASK)];
+
+ Pack.UInt32_To_LE(d_i ^ s_i, output, i * 4);
+ Pack.UInt32_To_LE(d_j ^ s_j, output, j * 4);
+ }
+
+ SparkleOpt(state, SPARKLE_STEPS_SLIM);
+
+ encrypted = true;
+ }
+
+ private void ProcessBufferEncrypt(ReadOnlySpan<byte> buffer, Span<byte> output)
+ {
+ Debug.Assert(buffer.Length >= RATE_BYTES);
+
+ Check.OutputLength(output, RATE_BYTES, "output buffer too short");
+
+ for (int i = 0; i < RATE_WORDS / 2; ++i)
+ {
+ int j = i + (RATE_WORDS / 2);
+
+ uint s_i = state[i];
+ uint s_j = state[j];
+
+ uint d_i = Pack.LE_To_UInt32(buffer, i * 4);
+ uint d_j = Pack.LE_To_UInt32(buffer, j * 4);
+
+ state[i] = s_j ^ d_i ^ state[RATE_WORDS + i];
+ state[j] = s_i ^ s_j ^ d_j ^ state[RATE_WORDS + (j & CAP_MASK)];
+
+ Pack.UInt32_To_LE(d_i ^ s_i, output, i * 4);
+ Pack.UInt32_To_LE(d_j ^ s_j, output, j * 4);
+ }
+
+ SparkleOpt(state, SPARKLE_STEPS_SLIM);
+
+ encrypted = true;
+ }
+#else
+ private void ProcessBufferAad(byte[] buffer, int bufOff)
+ {
+ for (int i = 0; i < RATE_WORDS / 2; ++i)
+ {
+ int j = i + (RATE_WORDS / 2);
+
+ uint s_i = state[i];
+ uint s_j = state[j];
+
+ uint d_i = Pack.LE_To_UInt32(buffer, bufOff + (i * 4));
+ uint d_j = Pack.LE_To_UInt32(buffer, bufOff + (j * 4));
+
+ state[i] = s_j ^ d_i ^ state[RATE_WORDS + i];
+ state[j] = s_i ^ s_j ^ d_j ^ state[RATE_WORDS + (j & CAP_MASK)];
+ }
+
+ SparkleOpt(state, SPARKLE_STEPS_SLIM);
+ }
+
+ private void ProcessBufferDecrypt(byte[] buffer, int bufOff, byte[] output, int outOff)
+ {
+ Debug.Assert(bufOff <= buffer.Length - RATE_BYTES);
+
+ Check.OutputLength(output, outOff, RATE_BYTES, "output buffer too short");
+
+ for (int i = 0; i < RATE_WORDS / 2; ++i)
+ {
+ int j = i + (RATE_WORDS / 2);
+
+ uint s_i = state[i];
+ uint s_j = state[j];
+
+ uint d_i = Pack.LE_To_UInt32(buffer, bufOff + (i * 4));
+ uint d_j = Pack.LE_To_UInt32(buffer, bufOff + (j * 4));
+
+ state[i] = s_i ^ s_j ^ d_i ^ state[RATE_WORDS + i];
+ state[j] = s_i ^ d_j ^ state[RATE_WORDS + (j & CAP_MASK)];
+
+ Pack.UInt32_To_LE(d_i ^ s_i, output, outOff + (i * 4));
+ Pack.UInt32_To_LE(d_j ^ s_j, output, outOff + (j * 4));
+ }
+
+ SparkleOpt(state, SPARKLE_STEPS_SLIM);
+
+ encrypted = true;
+ }
+
+ private void ProcessBufferEncrypt(byte[] buffer, int bufOff, byte[] output, int outOff)
+ {
+ Debug.Assert(bufOff <= buffer.Length - RATE_BYTES);
+
+ Check.OutputLength(output, outOff, RATE_BYTES, "output buffer too short");
+
+ for (int i = 0; i < RATE_WORDS / 2; ++i)
+ {
+ int j = i + (RATE_WORDS / 2);
+
+ uint s_i = state[i];
+ uint s_j = state[j];
+
+ uint d_i = Pack.LE_To_UInt32(buffer, bufOff + (i * 4));
+ uint d_j = Pack.LE_To_UInt32(buffer, bufOff + (j * 4));
+
+ state[i] = s_j ^ d_i ^ state[RATE_WORDS + i];
+ state[j] = s_i ^ s_j ^ d_j ^ state[RATE_WORDS + (j & CAP_MASK)];
+
+ Pack.UInt32_To_LE(d_i ^ s_i, output, outOff + (i * 4));
+ Pack.UInt32_To_LE(d_j ^ s_j, output, outOff + (j * 4));
+ }
+
+ SparkleOpt(state, SPARKLE_STEPS_SLIM);
+
+ encrypted = true;
+ }
+#endif
+
+ private void ProcessFinalAad()
+ {
+ // Authentication of Last Block
+
+ // addition of ant A0 or A1 to the state
+ state[STATE_WORDS - 1] ^= (m_bufPos < RATE_BYTES) ? _A0 : _A1;
+
+ // Rho and rate-whitening for the authentication of the last associated-data block.
+ uint[] buffer = new uint[RATE_WORDS];
+ for (int i = 0; i < m_bufPos; ++i)
+ {
+ buffer[i >> 2] |= (uint)m_buf[i] << ((i & 3) << 3);
+ }
+ if (m_bufPos < RATE_BYTES)
+ { // padding
+ buffer[m_bufPos >> 2] |= 0x80U << ((m_bufPos & 3) << 3);
+ }
+ for (int i = 0, j = RATE_WORDS / 2; i < RATE_WORDS / 2; i++, j++)
+ {
+ uint tmp = state[i];
+ state[i] = state[j] ^ buffer[i] ^ state[RATE_WORDS + i];
+ state[j] ^= tmp ^ buffer[j] ^ state[RATE_WORDS + (j & CAP_MASK)];
+ }
+
+ SparkleOpt(state, SPARKLE_STEPS_BIG);
+ }
+
+ private void Reset(bool clearMac)
+ {
+ if (clearMac)
+ {
+ tag = null;
+ }
+
+ Arrays.Clear(m_buf);
+ m_bufPos = 0;
+ encrypted = false;
+
+ switch (m_state)
+ {
+ case State.DecInit:
+ case State.EncInit:
+ break;
+ case State.DecAad:
+ case State.DecData:
+ case State.DecFinal:
+ m_state = State.DecInit;
+ break;
+ case State.EncAad:
+ case State.EncData:
+ case State.EncFinal:
+ m_state = State.EncFinal;
+ return;
+ default:
+ throw new InvalidOperationException(AlgorithmName + " needs to be initialized");
+ }
+
+ // The Initialize function loads nonce and key into the state and executes the
+ // SPARKLE permutation with the big number of steps.
+ // load nonce into the rate-part of the state
+ Array.Copy(npub, 0, state, 0, RATE_WORDS);
+ // load key into the capacity-part of the sate
+ Array.Copy(k, 0, state, RATE_WORDS, KEY_WORDS);
+
+ SparkleOpt(state, SPARKLE_STEPS_BIG);
+
+ if (initialAssociatedText != null)
+ {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ ProcessAadBytes(initialAssociatedText);
+#else
+ ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
+#endif
+ }
+ }
+
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private static void ArxBox(uint rc, ref uint s00, ref uint s01)
+ {
+ s00 += Integers.RotateRight(s01, 31);
+ s01 ^= Integers.RotateRight(s00, 24);
+ s00 ^= rc;
+ s00 += Integers.RotateRight(s01, 17);
+ s01 ^= Integers.RotateRight(s00, 17);
+ s00 ^= rc;
+ s00 += s01;
+ s01 ^= Integers.RotateRight(s00, 31);
+ s00 ^= rc;
+ s00 += Integers.RotateRight(s01, 24);
+ s01 ^= Integers.RotateRight(s00, 16);
+ s00 ^= rc;
+ }
+
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private static uint ELL(uint x)
+ {
+ return Integers.RotateRight(x, 16) ^ (x & 0xFFFFU);
+ }
+
+#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private static void SparkleOpt(uint[] state, int steps)
+ {
+ switch (state.Length)
+ {
+ case 8: SparkleOpt8 (state, steps); break;
+ case 12: SparkleOpt12(state, steps); break;
+ case 16: SparkleOpt16(state, steps); break;
+ default: throw new InvalidOperationException();
+ }
+ }
+
+ internal static void SparkleOpt8(uint[] state, int steps)
+ {
+ uint s00 = state[0];
+ uint s01 = state[1];
+ uint s02 = state[2];
+ uint s03 = state[3];
+ uint s04 = state[4];
+ uint s05 = state[5];
+ uint s06 = state[6];
+ uint s07 = state[7];
+
+ for (int step = 0; step < steps; ++step)
+ {
+ // Add round ant
+
+ s01 ^= RCON[step & 7];
+ s03 ^= (uint)step;
+
+ // ARXBOX layer
+
+ ArxBox(RCON[0], ref s00, ref s01);
+ ArxBox(RCON[1], ref s02, ref s03);
+ ArxBox(RCON[2], ref s04, ref s05);
+ ArxBox(RCON[3], ref s06, ref s07);
+
+ // Linear layer
+
+ uint t02 = ELL(s00 ^ s02);
+ uint t13 = ELL(s01 ^ s03);
+
+ uint u00 = s00 ^ s04;
+ uint u01 = s01 ^ s05;
+ uint u02 = s02 ^ s06;
+ uint u03 = s03 ^ s07;
+
+ s04 = s00;
+ s05 = s01;
+ s06 = s02;
+ s07 = s03;
+
+ s00 = u02 ^ t13;
+ s01 = u03 ^ t02;
+ s02 = u00 ^ t13;
+ s03 = u01 ^ t02;
+ }
+
+ state[0] = s00;
+ state[1] = s01;
+ state[2] = s02;
+ state[3] = s03;
+ state[4] = s04;
+ state[5] = s05;
+ state[6] = s06;
+ state[7] = s07;
+ }
+
+ internal static void SparkleOpt12(uint[] state, int steps)
+ {
+ uint s00 = state[0];
+ uint s01 = state[1];
+ uint s02 = state[2];
+ uint s03 = state[3];
+ uint s04 = state[4];
+ uint s05 = state[5];
+ uint s06 = state[6];
+ uint s07 = state[7];
+ uint s08 = state[8];
+ uint s09 = state[9];
+ uint s10 = state[10];
+ uint s11 = state[11];
+
+ for (int step = 0; step < steps; ++step)
+ {
+ // Add round ant
+
+ s01 ^= RCON[step & 7];
+ s03 ^= (uint)step;
+
+ // ARXBOX layer
+
+ ArxBox(RCON[0], ref s00, ref s01);
+ ArxBox(RCON[1], ref s02, ref s03);
+ ArxBox(RCON[2], ref s04, ref s05);
+ ArxBox(RCON[3], ref s06, ref s07);
+ ArxBox(RCON[4], ref s08, ref s09);
+ ArxBox(RCON[5], ref s10, ref s11);
+
+ // Linear layer
+
+ uint t024 = ELL(s00 ^ s02 ^ s04);
+ uint t135 = ELL(s01 ^ s03 ^ s05);
+
+ uint u00 = s00 ^ s06;
+ uint u01 = s01 ^ s07;
+ uint u02 = s02 ^ s08;
+ uint u03 = s03 ^ s09;
+ uint u04 = s04 ^ s10;
+ uint u05 = s05 ^ s11;
+
+ s06 = s00;
+ s07 = s01;
+ s08 = s02;
+ s09 = s03;
+ s10 = s04;
+ s11 = s05;
+
+ s00 = u02 ^ t135;
+ s01 = u03 ^ t024;
+ s02 = u04 ^ t135;
+ s03 = u05 ^ t024;
+ s04 = u00 ^ t135;
+ s05 = u01 ^ t024;
+ }
+
+ state[0] = s00;
+ state[1] = s01;
+ state[2] = s02;
+ state[3] = s03;
+ state[4] = s04;
+ state[5] = s05;
+ state[6] = s06;
+ state[7] = s07;
+ state[8] = s08;
+ state[9] = s09;
+ state[10] = s10;
+ state[11] = s11;
+ }
+
+ internal static void SparkleOpt16(uint[] state, int steps)
+ {
+ Debug.Assert((steps & 1) == 0);
+
+#if NETCOREAPP3_0_OR_GREATER
+ if (Sse2.IsSupported)
+ {
+ var s0246 = Vector128.Create(state[0], state[2], state[4], state[6]);
+ var s1357 = Vector128.Create(state[1], state[3], state[5], state[7]);
+ var s8ACE = Vector128.Create(state[8], state[10], state[12], state[14]);
+ var s9BDF = Vector128.Create(state[9], state[11], state[13], state[15]);
+
+ var RC03 = Load128(RCON.AsSpan(0));
+ var RC47 = Load128(RCON.AsSpan(4));
+
+ for (int step = 0; step < steps; ++step)
+ {
+ // Add round ant
+
+ s1357 = Sse2.Xor(s1357, Vector128.Create(RCON[step & 7], (uint)step, 0U, 0U));
+
+ // ARXBOX layer
+
+ ArxBox(RC03, ref s0246, ref s1357);
+ ArxBox(RC47, ref s8ACE, ref s9BDF);
+
+ // Linear layer
+
+ var t0246 = ELL(HorizontalXor(s0246));
+ var t1357 = ELL(HorizontalXor(s1357));
+
+ var u0246 = Sse2.Xor(s0246, s8ACE);
+ var u1357 = Sse2.Xor(s1357, s9BDF);
+
+ s8ACE = s0246;
+ s9BDF = s1357;
+
+ s0246 = Sse2.Xor(t1357, Sse2.Shuffle(u0246, 0x39));
+ s1357 = Sse2.Xor(t0246, Sse2.Shuffle(u1357, 0x39));
+ }
+
+ Store128(Sse2.UnpackLow (s0246, s1357), state.AsSpan(0));
+ Store128(Sse2.UnpackHigh(s0246, s1357), state.AsSpan(4));
+ Store128(Sse2.UnpackLow (s8ACE, s9BDF), state.AsSpan(8));
+ Store128(Sse2.UnpackHigh(s8ACE, s9BDF), state.AsSpan(12));
+ }
+ else
+#endif
+ {
+ uint s00 = state[0];
+ uint s01 = state[1];
+ uint s02 = state[2];
+ uint s03 = state[3];
+ uint s04 = state[4];
+ uint s05 = state[5];
+ uint s06 = state[6];
+ uint s07 = state[7];
+ uint s08 = state[8];
+ uint s09 = state[9];
+ uint s10 = state[10];
+ uint s11 = state[11];
+ uint s12 = state[12];
+ uint s13 = state[13];
+ uint s14 = state[14];
+ uint s15 = state[15];
+
+ int step = 0;
+ while (step < steps)
+ {
+ // STEP 1
+
+ // Add round ant
+
+ s01 ^= RCON[step & 7];
+ s03 ^= (uint)(step++);
+
+ // ARXBOX layer
+
+ ArxBox(RCON[0], ref s00, ref s01);
+ ArxBox(RCON[1], ref s02, ref s03);
+ ArxBox(RCON[2], ref s04, ref s05);
+ ArxBox(RCON[3], ref s06, ref s07);
+ ArxBox(RCON[4], ref s08, ref s09);
+ ArxBox(RCON[5], ref s10, ref s11);
+ ArxBox(RCON[6], ref s12, ref s13);
+ ArxBox(RCON[7], ref s14, ref s15);
+
+ // Linear layer
+
+ uint t0246 = ELL(s00 ^ s02 ^ s04 ^ s06);
+ uint t1357 = ELL(s01 ^ s03 ^ s05 ^ s07);
+
+ uint u08 = s08;
+ uint u09 = s09;
+
+ s08 = s02 ^ s10 ^ t1357;
+ s09 = s03 ^ s11 ^ t0246;
+ s10 = s04 ^ s12 ^ t1357;
+ s11 = s05 ^ s13 ^ t0246;
+ s12 = s06 ^ s14 ^ t1357;
+ s13 = s07 ^ s15 ^ t0246;
+ s14 = s00 ^ u08 ^ t1357;
+ s15 = s01 ^ u09 ^ t0246;
+
+ // STEP 2
+
+ // Add round ant
+
+ s09 ^= RCON[step & 7];
+ s11 ^= (uint)(step++);
+
+ // ARXBOX layer
+
+ ArxBox(RCON[0], ref s08, ref s09);
+ ArxBox(RCON[1], ref s10, ref s11);
+ ArxBox(RCON[2], ref s12, ref s13);
+ ArxBox(RCON[3], ref s14, ref s15);
+ ArxBox(RCON[4], ref s00, ref s01);
+ ArxBox(RCON[5], ref s02, ref s03);
+ ArxBox(RCON[6], ref s04, ref s05);
+ ArxBox(RCON[7], ref s06, ref s07);
+
+ // Linear layer
+
+ uint t8ACE = ELL(s08 ^ s10 ^ s12 ^ s14);
+ uint t9BDF = ELL(s09 ^ s11 ^ s13 ^ s15);
+
+ uint u00 = s00;
+ uint u01 = s01;
+
+ s00 = s02 ^ s10 ^ t9BDF;
+ s01 = s03 ^ s11 ^ t8ACE;
+ s02 = s04 ^ s12 ^ t9BDF;
+ s03 = s05 ^ s13 ^ t8ACE;
+ s04 = s06 ^ s14 ^ t9BDF;
+ s05 = s07 ^ s15 ^ t8ACE;
+ s06 = u00 ^ s08 ^ t9BDF;
+ s07 = u01 ^ s09 ^ t8ACE;
+ }
+
+ state[0] = s00;
+ state[1] = s01;
+ state[2] = s02;
+ state[3] = s03;
+ state[4] = s04;
+ state[5] = s05;
+ state[6] = s06;
+ state[7] = s07;
+ state[8] = s08;
+ state[9] = s09;
+ state[10] = s10;
+ state[11] = s11;
+ state[12] = s12;
+ state[13] = s13;
+ state[14] = s14;
+ state[15] = s15;
+ }
+ }
+
+#if NETCOREAPP3_0_OR_GREATER
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void ArxBox(Vector128<uint> rc, ref Vector128<uint> s00, ref Vector128<uint> s01)
+ {
+ s00 = Sse2.Add(s00, Sse2.ShiftRightLogical(s01, 31));
+ s00 = Sse2.Add(s00, Sse2.ShiftLeftLogical(s01, 1));
+
+ s01 = Sse2.Xor(s01, Sse2.ShiftRightLogical(s00, 24));
+ s01 = Sse2.Xor(s01, Sse2.ShiftLeftLogical(s00, 8));
+
+ s00 = Sse2.Xor(s00, rc);
+
+ s00 = Sse2.Add(s00, Sse2.ShiftRightLogical(s01, 17));
+ s00 = Sse2.Add(s00, Sse2.ShiftLeftLogical(s01, 15));
+
+ s01 = Sse2.Xor(s01, Sse2.ShiftRightLogical(s00, 17));
+ s01 = Sse2.Xor(s01, Sse2.ShiftLeftLogical(s00, 15));
+
+ s00 = Sse2.Xor(s00, rc);
+
+ s00 = Sse2.Add(s00, s01);
+
+ s01 = Sse2.Xor(s01, Sse2.ShiftRightLogical(s00, 31));
+ s01 = Sse2.Xor(s01, Sse2.ShiftLeftLogical(s00, 1));
+
+ s00 = Sse2.Xor(s00, rc);
+
+ s00 = Sse2.Add(s00, Sse2.ShiftRightLogical(s01, 24));
+ s00 = Sse2.Add(s00, Sse2.ShiftLeftLogical(s01, 8));
+
+ s01 = Sse2.Xor(s01, Sse2.ShiftRightLogical(s00, 16));
+ s01 = Sse2.Xor(s01, Sse2.ShiftLeftLogical(s00, 16));
+
+ s00 = Sse2.Xor(s00, rc);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static Vector128<uint> ELL(Vector128<uint> x)
+ {
+ var t = Sse2.ShiftLeftLogical(x, 16);
+ var u = Sse2.Xor(x, t);
+ return Sse2.Xor(t, Sse2.ShiftRightLogical(u, 16));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static Vector128<uint> HorizontalXor(Vector128<uint> x)
+ {
+ var t = Sse2.Xor(x, Sse2.Shuffle(x, 0x1B));
+ return Sse2.Xor(t, Sse2.Shuffle(t, 0xB1));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static Vector128<uint> Load128(ReadOnlySpan<uint> t)
+ {
+ if (BitConverter.IsLittleEndian && Unsafe.SizeOf<Vector128<uint>>() == 16)
+ return MemoryMarshal.Read<Vector128<uint>>(MemoryMarshal.AsBytes(t));
+
+ return Vector128.Create(t[0], t[1], t[2], t[3]);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void Store128(Vector128<uint> s, Span<uint> t)
+ {
+ var b = MemoryMarshal.AsBytes(t);
+ if (BitConverter.IsLittleEndian && Unsafe.SizeOf<Vector128<uint>>() == 16)
+ {
+ MemoryMarshal.Write(b, ref s);
+ return;
+ }
+
+ var u = s.AsUInt64();
+ BinaryPrimitives.WriteUInt64LittleEndian(b[..8], u.GetElement(0));
+ BinaryPrimitives.WriteUInt64LittleEndian(b[8..], u.GetElement(1));
+ }
+#endif
+ }
+}
diff --git a/crypto/test/src/crypto/test/SparkleTest.cs b/crypto/test/src/crypto/test/SparkleTest.cs
index 584b90a52..49d40aa15 100644
--- a/crypto/test/src/crypto/test/SparkleTest.cs
+++ b/crypto/test/src/crypto/test/SparkleTest.cs
@@ -5,6 +5,8 @@ using System.IO;
using NUnit.Framework;
using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Encoders;
using Org.BouncyCastle.Utilities.Test;
@@ -15,46 +17,207 @@ namespace Org.BouncyCastle.Crypto.Tests
public class SparkleTest
{
[Test, Explicit]
- public void BenchDigest256()
+ public void BenchDigest_ESCH256()
{
ImplBenchDigest(SparkleDigest.SparkleParameters.ESCH256);
}
[Test, Explicit]
- public void BenchDigest384()
+ public void BenchDigest_ESCH384()
{
ImplBenchDigest(SparkleDigest.SparkleParameters.ESCH384);
}
+ [Test, Explicit]
+ public void BenchEngineAuth_SCHWAEMM128_128()
+ {
+ ImplBenchEngineAuth(SparkleEngine.SparkleParameters.SCHWAEMM128_128);
+ }
+
+ [Test, Explicit]
+ public void BenchEngineAuth_SCHWAEMM192_192()
+ {
+ ImplBenchEngineAuth(SparkleEngine.SparkleParameters.SCHWAEMM192_192);
+ }
+
+ [Test, Explicit]
+ public void BenchEngineAuth_SCHWAEMM256_128()
+ {
+ ImplBenchEngineAuth(SparkleEngine.SparkleParameters.SCHWAEMM256_128);
+ }
+
+ [Test, Explicit]
+ public void BenchEngineAuth_SCHWAEMM256_256()
+ {
+ ImplBenchEngineAuth(SparkleEngine.SparkleParameters.SCHWAEMM256_256);
+ }
+
+ [Test, Explicit]
+ public void BenchEngineDecrypt_SCHWAEMM128_128()
+ {
+ ImplBenchEngineCrypt(SparkleEngine.SparkleParameters.SCHWAEMM128_128, false);
+ }
+
+ [Test, Explicit]
+ public void BenchEngineDecrypt_SCHWAEMM192_192()
+ {
+ ImplBenchEngineCrypt(SparkleEngine.SparkleParameters.SCHWAEMM192_192, false);
+ }
+
+ [Test, Explicit]
+ public void BenchEngineDecrypt_SCHWAEMM256_128()
+ {
+ ImplBenchEngineCrypt(SparkleEngine.SparkleParameters.SCHWAEMM256_128, false);
+ }
+
+ [Test, Explicit]
+ public void BenchEngineDecrypt_SCHWAEMM256_256()
+ {
+ ImplBenchEngineCrypt(SparkleEngine.SparkleParameters.SCHWAEMM256_256, false);
+ }
+
+ [Test, Explicit]
+ public void BenchEngineEncrypt_SCHWAEMM128_128()
+ {
+ ImplBenchEngineCrypt(SparkleEngine.SparkleParameters.SCHWAEMM128_128, true);
+ }
+
+ [Test, Explicit]
+ public void BenchEngineEncrypt_SCHWAEMM192_192()
+ {
+ ImplBenchEngineCrypt(SparkleEngine.SparkleParameters.SCHWAEMM192_192, true);
+ }
+
+ [Test, Explicit]
+ public void BenchEngineEncrypt_SCHWAEMM256_128()
+ {
+ ImplBenchEngineCrypt(SparkleEngine.SparkleParameters.SCHWAEMM256_128, true);
+ }
+
+ [Test, Explicit]
+ public void BenchEngineEncrypt_SCHWAEMM256_256()
+ {
+ ImplBenchEngineCrypt(SparkleEngine.SparkleParameters.SCHWAEMM256_256, true);
+ }
+
+ [Test]
+ public void TestExceptionsDigest_ESCH256()
+ {
+ ImplTestExceptionsDigest(SparkleDigest.SparkleParameters.ESCH256);
+ }
+
+ [Test]
+ public void TestExceptionsDigest_ESCH384()
+ {
+ ImplTestExceptionsDigest(SparkleDigest.SparkleParameters.ESCH384);
+ }
+
+ [Test]
+ public void TestExceptionsEngine_SCHWAEMM128_128()
+ {
+ ImplTestExceptionsEngine(SparkleEngine.SparkleParameters.SCHWAEMM128_128);
+ }
+
+ [Test]
+ public void TestExceptionsEngine_SCHWAEMM192_192()
+ {
+ ImplTestExceptionsEngine(SparkleEngine.SparkleParameters.SCHWAEMM192_192);
+ }
+
+ [Test]
+ public void TestExceptionsEngine_SCHWAEMM256_128()
+ {
+ ImplTestExceptionsEngine(SparkleEngine.SparkleParameters.SCHWAEMM256_128);
+ }
+
+ [Test]
+ public void TestExceptionsEngine_SCHWAEMM256_256()
+ {
+ ImplTestExceptionsEngine(SparkleEngine.SparkleParameters.SCHWAEMM256_256);
+ }
+
+ [Test]
+ public void TestParametersDigest_ESCH256()
+ {
+ ImplTestParametersDigest(SparkleDigest.SparkleParameters.ESCH256, 32);
+ }
+
[Test]
- public void TestExceptionsDigest256()
+ public void TestParametersDigest_ESCH384()
{
- ImplTestExceptionsDigest(SparkleDigest.SparkleParameters.ESCH256, 32);
+ ImplTestParametersDigest(SparkleDigest.SparkleParameters.ESCH384, 48);
}
[Test]
- public void TestExceptionsDigest384()
+ public void TestParametersEngine_SCHWAEMM128_128()
{
- ImplTestExceptionsDigest(SparkleDigest.SparkleParameters.ESCH384, 48);
+ ImplTestParametersEngine(SparkleEngine.SparkleParameters.SCHWAEMM128_128, 16, 16, 16);
}
[Test]
- public void TestVectorsDigest256()
+ public void TestParametersEngine_SCHWAEMM192_192()
+ {
+ ImplTestParametersEngine(SparkleEngine.SparkleParameters.SCHWAEMM192_192, 24, 24, 24);
+ }
+
+ [Test]
+ public void TestParametersEngine_SCHWAEMM256_128()
+ {
+ ImplTestParametersEngine(SparkleEngine.SparkleParameters.SCHWAEMM256_128, 16, 32, 16);
+ }
+
+ [Test]
+ public void TestParametersEngine_SCHWAEMM256_256()
+ {
+ ImplTestParametersEngine(SparkleEngine.SparkleParameters.SCHWAEMM256_256, 32, 32, 32);
+ }
+
+ [Test]
+ public void TestVectorsDigest_ESCH256()
{
ImplTestVectorsDigest(SparkleDigest.SparkleParameters.ESCH256, "256");
}
[Test]
- public void TestVectorsDigest384()
+ public void TestVectorsDigest_ESCH384()
{
ImplTestVectorsDigest(SparkleDigest.SparkleParameters.ESCH384, "384");
}
- private static IDigest CreateDigest(SparkleDigest.SparkleParameters sparkleParameters)
+ [Test]
+ public void TestVectorsEngine_SCHWAEMM128_128()
+ {
+ ImplTestVectorsEngine(SparkleEngine.SparkleParameters.SCHWAEMM128_128, "128_128");
+ }
+
+ [Test]
+ public void TestVectorsEngine_SCHWAEMM192_192()
+ {
+ ImplTestVectorsEngine(SparkleEngine.SparkleParameters.SCHWAEMM192_192, "192_192");
+ }
+
+ [Test]
+ public void TestVectorsEngine_SCHWAEMM256_128()
+ {
+ ImplTestVectorsEngine(SparkleEngine.SparkleParameters.SCHWAEMM256_128, "128_256");
+ }
+
+ [Test]
+ public void TestVectorsEngine_SCHWAEMM256_256()
+ {
+ ImplTestVectorsEngine(SparkleEngine.SparkleParameters.SCHWAEMM256_256, "256_256");
+ }
+
+ private static SparkleDigest CreateDigest(SparkleDigest.SparkleParameters sparkleParameters)
{
return new SparkleDigest(sparkleParameters);
}
+ private static SparkleEngine CreateEngine(SparkleEngine.SparkleParameters sparkleParameters)
+ {
+ return new SparkleEngine(sparkleParameters);
+ }
+
private static void ImplBenchDigest(SparkleDigest.SparkleParameters sparkleParameters)
{
var sparkle = CreateDigest(sparkleParameters);
@@ -83,11 +246,437 @@ namespace Org.BouncyCastle.Crypto.Tests
}
}
+ private static void ImplBenchEngineAuth(SparkleEngine.SparkleParameters sparkleParameters)
+ {
+ var sparkle = CreateEngine(sparkleParameters);
+ InitEngine(sparkle, true);
+
+ byte[] data = new byte[1024];
+ for (int i = 0; i < 1024 * 1024; ++i)
+ {
+ // NOTE: .NET Core 3.1 has Span<T>, but is tested against our .NET Standard 2.0 assembly.
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ sparkle.ProcessAadBytes(data);
+#else
+ sparkle.ProcessAadBytes(data, 0, 1024);
+#endif
+ }
+ }
+
+ private static void ImplBenchEngineCrypt(SparkleEngine.SparkleParameters sparkleParameters, bool forEncryption)
+ {
+ var sparkle = CreateEngine(sparkleParameters);
+ InitEngine(sparkle, forEncryption);
+
+ byte[] data = new byte[1024 + 64];
+ for (int i = 0; i < 1024 * 1024; ++i)
+ {
+ // NOTE: .NET Core 3.1 has Span<T>, but is tested against our .NET Standard 2.0 assembly.
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ sparkle.ProcessBytes(data.AsSpan(0, 1024), data);
+#else
+ sparkle.ProcessBytes(data, 0, 1024, data, 0);
+#endif
+ }
+ }
+
+ private static void ImplTestExceptionsDigest(SparkleDigest.SparkleParameters sparkleParameters)
+ {
+ var sparkle = new SparkleDigest(sparkleParameters);
+
+ 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[sparkle.GetDigestSize() - 1], 2);
+ Assert.Fail(sparkle.AlgorithmName + ": output for Dofinal is too short");
+ }
+ catch (DataLengthException)
+ {
+ //expected
+ }
+ }
+
+ private void ImplTestExceptionsEngine(SparkleEngine.SparkleParameters sparkleParameters)
+ {
+ var sparkle = new SparkleEngine(sparkleParameters);
+
+ int keysize = sparkle.GetKeyBytesSize(), ivsize = sparkle.GetIVBytesSize();
+ int offset;
+ byte[] k = new byte[keysize];
+ byte[] iv = new byte[ivsize];
+ byte[] m = Array.Empty<byte>();
+ var param = new ParametersWithIV(new KeyParameter(k), iv);
+ try
+ {
+ sparkle.ProcessBytes(m, 0, m.Length, null, 0);
+ Assert.Fail(sparkle.AlgorithmName + " needs to be initialized before ProcessBytes");
+ }
+ catch (InvalidOperationException)
+ {
+ //expected
+ }
+
+ try
+ {
+ sparkle.ProcessByte(0x00, null, 0);
+ Assert.Fail(sparkle.AlgorithmName + " needs to be initialized before ProcessByte");
+ }
+ catch (InvalidOperationException)
+ {
+ //expected
+ }
+
+ try
+ {
+ sparkle.Reset();
+ Assert.Fail(sparkle.AlgorithmName + " needs to be initialized before Reset");
+ }
+ catch (InvalidOperationException)
+ {
+ //expected
+ }
+
+ try
+ {
+ sparkle.DoFinal(null, m.Length);
+ Assert.Fail(sparkle.AlgorithmName + " needs to be initialized before DoFinal");
+ }
+ catch (InvalidOperationException)
+ {
+ //expected
+ }
+
+ try
+ {
+ sparkle.GetMac();
+ sparkle.GetOutputSize(0);
+ sparkle.GetUpdateOutputSize(0);
+ }
+ catch (InvalidOperationException)
+ {
+ //expected
+ Assert.Fail(sparkle.AlgorithmName + " functions can be called before initialization");
+ }
+
+ Random rand = new Random();
+ int randomNum;
+ while ((randomNum = rand.Next(100)) == keysize) ;
+ byte[] k1 = new byte[randomNum];
+ while ((randomNum = rand.Next(100)) == ivsize) ;
+ byte[] iv1 = new byte[randomNum];
+ try
+ {
+ sparkle.Init(true, new ParametersWithIV(new KeyParameter(k1), iv));
+ Assert.Fail(sparkle.AlgorithmName + " k size does not match");
+ }
+ catch (ArgumentException)
+ {
+ //expected
+ }
+ try
+ {
+ sparkle.Init(true, new ParametersWithIV(new KeyParameter(k), iv1));
+ Assert.Fail(sparkle.AlgorithmName + "iv size does not match");
+ }
+ catch (ArgumentException)
+ {
+ //expected
+ }
+
+ sparkle.Init(true, param);
+ byte[] c1 = new byte[sparkle.GetOutputSize(m.Length)];
+ try
+ {
+ sparkle.DoFinal(c1, m.Length);
+ }
+ catch (Exception)
+ {
+ Assert.Fail(sparkle.AlgorithmName + " allows no input for AAD and plaintext");
+ }
+ byte[] mac2 = sparkle.GetMac();
+ if (mac2 == null)
+ {
+ Assert.Fail("mac should not be empty after DoFinal");
+ }
+ if (!Arrays.AreEqual(mac2, c1))
+ {
+ Assert.Fail("mac should be equal when calling DoFinal and GetMac");
+ }
+
+ // TODO Maybe use a different IV for this
+ sparkle.Init(true, param);
+ sparkle.ProcessAadByte((byte)0);
+ byte[] mac1 = new byte[sparkle.GetOutputSize(0)];
+ sparkle.DoFinal(mac1, 0);
+ if (Arrays.AreEqual(mac1, mac2))
+ {
+ Assert.Fail("mac should not match");
+ }
+
+ // TODO Maybe use a different IV for this
+ sparkle.Init(true, param);
+ sparkle.ProcessByte(0, null, 0);
+ try
+ {
+ sparkle.ProcessAadByte(0x00);
+ Assert.Fail("ProcessAadByte cannot be called after encryption/decryption");
+ }
+ catch (InvalidOperationException)
+ {
+ //expected
+ }
+ try
+ {
+ sparkle.ProcessAadBytes(new byte[1], 0, 1);
+ Assert.Fail("ProcessAadBytes cannot be called after encryption/decryption");
+ }
+ catch (InvalidOperationException)
+ {
+ //expected
+ }
+
+ // TODO Maybe use a different IV for this
+ sparkle.Init(true, param);
+ try
+ {
+ sparkle.ProcessAadBytes(new byte[1], 1, 1);
+ Assert.Fail("input for ProcessAadBytes is too short");
+ }
+ catch (DataLengthException)
+ {
+ //expected
+ }
+ try
+ {
+ sparkle.ProcessBytes(new byte[1], 1, 1, c1, 0);
+ Assert.Fail("input for ProcessBytes is too short");
+ }
+ catch (DataLengthException)
+ {
+ //expected
+ }
+ try
+ {
+ sparkle.DoFinal(new byte[2], 2);
+ Assert.Fail("output for DoFinal is too short");
+ }
+ catch (OutputLengthException)
+ {
+ //expected
+ }
+
+ ImplTestExceptionsGetUpdateOutputSize(sparkle, false, param, 100);
+ ImplTestExceptionsGetUpdateOutputSize(sparkle, true, param, 100);
+
+ mac1 = new byte[sparkle.GetOutputSize(0)];
+ mac2 = new byte[sparkle.GetOutputSize(0)];
+
+ // TODO Maybe use a different IV for this
+ sparkle.Init(true, param);
+ sparkle.ProcessAadBytes(new byte[2], 0, 2);
+ sparkle.DoFinal(mac1, 0);
+
+ // TODO Maybe use a different IV for this
+ sparkle.Init(true, param);
+ sparkle.ProcessAadByte(0x00);
+ sparkle.ProcessAadByte(0x00);
+ sparkle.DoFinal(mac2, 0);
+
+ if (!Arrays.AreEqual(mac1, mac2))
+ {
+ Assert.Fail("mac should match for the same AAD with different ways of inputting");
+ }
+
+ byte[] aad2 = { 0, 1, 2, 3, 4 };
+ byte[] aad3 = { 0, 0, 1, 2, 3, 4, 5 };
+ byte[] m2 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ byte[] m3 = { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ byte[] m4 = new byte[m2.Length];
+ byte[] c2 = new byte[sparkle.GetOutputSize(m2.Length)];
+ byte[] c3 = new byte[sparkle.GetOutputSize(m3.Length)];
+
+ // TODO Maybe use a different IV for this
+ sparkle.Init(true, param);
+ sparkle.ProcessAadBytes(aad2, 0, aad2.Length);
+ offset = sparkle.ProcessBytes(m2, 0, m2.Length, c2, 0);
+ sparkle.DoFinal(c2, offset);
+
+ // TODO Maybe use a different IV for this
+ sparkle.Init(true, param);
+ sparkle.ProcessAadBytes(aad3, 1, aad2.Length);
+ offset = sparkle.ProcessBytes(m3, 1, m2.Length, c3, 1);
+ sparkle.DoFinal(c3, offset + 1);
+
+ byte[] c3_partial = new byte[c2.Length];
+ Array.Copy(c3, 1, c3_partial, 0, c2.Length);
+ if (!Arrays.AreEqual(c2, c3_partial))
+ {
+ Assert.Fail("mac should match for the same AAD and message with different offset for both input and output");
+ }
+
+ sparkle.Init(false, param);
+ sparkle.ProcessAadBytes(aad2, 0, aad2.Length);
+ offset = sparkle.ProcessBytes(c2, 0, c2.Length, m4, 0);
+ sparkle.DoFinal(m4, offset);
+ if (!Arrays.AreEqual(m2, m4))
+ {
+ Assert.Fail("The encryption and decryption does not recover the plaintext");
+ }
+
+ c2[c2.Length - 1] ^= 1;
+
+ sparkle.ProcessAadBytes(aad2, 0, aad2.Length);
+ offset = sparkle.ProcessBytes(c2, 0, c2.Length, m4, 0);
+ try
+ {
+ sparkle.DoFinal(m4, offset);
+ Assert.Fail("The decryption should fail");
+ }
+ catch (InvalidCipherTextException)
+ {
+ //expected;
+ }
+ c2[c2.Length - 1] ^= 1;
+
+ byte[] m7 = new byte[32 + rand.Next(32)];
+ rand.NextBytes(m7);
+
+ // TODO Maybe use a different IV for this
+ sparkle.Init(true, param);
+ byte[] c7 = new byte[sparkle.GetOutputSize(m7.Length)];
+ byte[] c8 = new byte[c7.Length];
+ byte[] c9 = new byte[c7.Length];
+ sparkle.Init(true, param);
+ sparkle.ProcessAadBytes(aad2, 0, aad2.Length);
+ offset = sparkle.ProcessBytes(m7, 0, m7.Length, c7, 0);
+ sparkle.DoFinal(c7, offset);
+
+ // TODO Maybe use a different IV for this
+ sparkle.Init(true, param);
+ sparkle.ProcessAadBytes(aad2, 0, aad2.Length);
+ offset = sparkle.ProcessBytes(m7, 0, m7.Length / 2, c8, 0);
+ offset += sparkle.ProcessBytes(m7, m7.Length / 2, m7.Length - m7.Length / 2, c8, offset);
+ offset += sparkle.DoFinal(c8, offset);
+
+ // TODO Maybe use a different IV for this
+ sparkle.Init(true, param);
+ int split = rand.Next(1, m7.Length);
+ sparkle.ProcessAadBytes(aad2, 0, aad2.Length);
+ offset = sparkle.ProcessBytes(m7, 0, split, c9, 0);
+ offset += sparkle.ProcessBytes(m7, split, m7.Length - split, c9, offset);
+ offset += sparkle.DoFinal(c9, offset);
+ if (!Arrays.AreEqual(c7, c8) || !Arrays.AreEqual(c7, c9))
+ {
+ Assert.Fail("Splitting input of plaintext should output the same ciphertext");
+ }
+
+ // NOTE: .NET Core 3.1 has Span<T>, but is tested against our .NET Standard 2.0 assembly.
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ // TODO Maybe use a different IV for this
+ sparkle.Init(true, param);
+ Span<byte> c4 = new byte[sparkle.GetOutputSize(m2.Length)];
+ sparkle.ProcessAadBytes(aad2);
+ offset = sparkle.ProcessBytes(m2, c4);
+ offset += sparkle.DoFinal(c4[offset..]);
+ if (!c4[..offset].SequenceEqual(c2))
+ {
+ Assert.Fail("Encryption should match for the same AAD and message with/without Span-based API");
+ }
+
+ sparkle.Init(false, param);
+ Span<byte> m6 = new byte[m2.Length];
+ sparkle.ProcessAadBytes(aad2);
+ offset = sparkle.ProcessBytes(c2, m6);
+ offset += sparkle.DoFinal(m6[offset..]);
+ if (!m6[..offset].SequenceEqual(m2))
+ {
+ Assert.Fail("Decryption should match for the same AAD and message with/without Span-based API");
+ }
+#endif
+ }
+
+ private static void ImplTestExceptionsGetUpdateOutputSize(SparkleEngine sparkle, bool forEncryption,
+ ICipherParameters parameters, int maxInputSize)
+ {
+ // TODO Maybe use a different IV for this
+ sparkle.Init(forEncryption, parameters);
+
+ int maxOutputSize = sparkle.GetUpdateOutputSize(maxInputSize);
+
+ byte[] input = new byte[maxInputSize];
+ byte[] output = new byte[maxOutputSize];
+
+ for (int inputSize = 0; inputSize <= maxInputSize; ++inputSize)
+ {
+ // TODO Maybe use a different IV for this
+ sparkle.Init(forEncryption, parameters);
+
+ int outputSize = sparkle.GetUpdateOutputSize(inputSize);
+ if (outputSize > 0)
+ {
+ try
+ {
+ sparkle.ProcessBytes(input, 0, inputSize, output, maxOutputSize - outputSize + 1);
+ Assert.Fail("output for ProcessBytes is too short");
+ }
+ catch (OutputLengthException)
+ {
+ //expected
+ }
+ }
+ else
+ {
+ sparkle.ProcessBytes(input, 0, inputSize, null, 0);
+ }
+ }
+ }
+
+ private static void ImplTestParametersDigest(SparkleDigest.SparkleParameters sparkleParameters, int digestSize)
+ {
+ var sparkle = CreateDigest(sparkleParameters);
+
+ Assert.AreEqual(digestSize, sparkle.GetDigestSize(),
+ sparkle.AlgorithmName + ": GetDigestSize() is not correct");
+ }
+
+ private static void ImplTestParametersEngine(SparkleEngine.SparkleParameters sparkleParameters, int keySize,
+ int ivSize, int macSize)
+ {
+ var sparkle = CreateEngine(sparkleParameters);
+
+ Assert.AreEqual(keySize, sparkle.GetKeyBytesSize(),
+ "key bytes of " + sparkle.AlgorithmName + " is not correct");
+ Assert.AreEqual(ivSize, sparkle.GetIVBytesSize(),
+ "iv bytes of " + sparkle.AlgorithmName + " is not correct");
+
+ var parameters = new ParametersWithIV(new KeyParameter(new byte[keySize]), new byte[ivSize]);
+
+ sparkle.Init(true, parameters);
+ Assert.AreEqual(macSize, sparkle.GetOutputSize(0),
+ "GetOutputSize of " + sparkle.AlgorithmName + " is incorrect for encryption");
+
+ sparkle.Init(false, parameters);
+ Assert.AreEqual(0, sparkle.GetOutputSize(macSize),
+ "GetOutputSize of " + sparkle.AlgorithmName + " is incorrect for decryption");
+ }
+
private static void ImplTestVectorsDigest(SparkleDigest.SparkleParameters sparkleParameters, string filename)
{
Random random = new Random();
var sparkle = CreateDigest(sparkleParameters);
- var map = new Dictionary<string, string>();
+ var buf = new Dictionary<string, string>();
using (var src = new StreamReader(
SimpleTest.GetTestDataAsStream("crypto.sparkle.LWC_HASH_KAT_" + filename + ".txt")))
{
@@ -97,9 +686,9 @@ namespace Org.BouncyCastle.Crypto.Tests
int a = line.IndexOf('=');
if (a < 0)
{
- byte[] ptByte = Hex.Decode(map["Msg"]);
- byte[] expected = Hex.Decode(map["MD"]);
- map.Clear();
+ byte[] ptByte = Hex.Decode(buf["Msg"]);
+ byte[] expected = Hex.Decode(buf["MD"]);
+ buf.Clear();
byte[] hash = new byte[sparkle.GetDigestSize()];
@@ -118,37 +707,87 @@ namespace Org.BouncyCastle.Crypto.Tests
}
else
{
- map[line.Substring(0, a).Trim()] = line.Substring(a + 1).Trim();
+ buf[line.Substring(0, a).Trim()] = line.Substring(a + 1).Trim();
}
}
}
}
- private static void ImplTestExceptionsDigest(SparkleDigest.SparkleParameters sparkleParameters, int digestSize)
+ private static void ImplTestVectorsEngine(SparkleEngine.SparkleParameters sparkleParameters, string filename)
{
- var sparkle = new SparkleDigest(sparkleParameters);
+ Random random = new Random();
+ var sparkle = CreateEngine(sparkleParameters);
+ var buf = new Dictionary<string, string>();
+ using (var src = new StreamReader(
+ SimpleTest.GetTestDataAsStream("crypto.sparkle.LWC_AEAD_KAT_" + filename + ".txt")))
+ {
+ string line;
+ while ((line = src.ReadLine()) != null)
+ {
+ var data = line.Split(' ');
+ if (data.Length == 1)
+ {
+ byte[] key = Hex.Decode(buf["Key"]);
+ byte[] nonce = Hex.Decode(buf["Nonce"]);
+ byte[] ad = Hex.Decode(buf["AD"]);
+ byte[] pt = Hex.Decode(buf["PT"]);
+ byte[] ct = Hex.Decode(buf["CT"]);
+ buf.Clear();
- Assert.AreEqual(digestSize, sparkle.GetDigestSize(),
- sparkle.AlgorithmName + ": GetDigestSize() is not correct");
+ var parameters = new ParametersWithIV(new KeyParameter(key), nonce);
- 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
+ // Encrypt
+ {
+ sparkle.Init(true, parameters);
+
+ var rv = new byte[sparkle.GetOutputSize(pt.Length)];
+ random.NextBytes(rv); // should overwrite any existing data
+
+ sparkle.ProcessAadBytes(ad, 0, ad.Length);
+ int len = sparkle.ProcessBytes(pt, 0, pt.Length, rv, 0);
+ len += sparkle.DoFinal(rv, len);
+
+ Assert.True(Arrays.AreEqual(rv, 0, len, ct, 0, ct.Length));
+ }
+
+ // Decrypt
+ {
+ sparkle.Init(false, parameters);
+
+ var rv = new byte[sparkle.GetOutputSize(ct.Length)];
+ random.NextBytes(rv); // should overwrite any existing data
+
+ sparkle.ProcessAadBytes(ad, 0, ad.Length);
+ int len = sparkle.ProcessBytes(ct, 0, ct.Length, rv, 0);
+ len += sparkle.DoFinal(rv, len);
+
+ Assert.True(Arrays.AreEqual(rv, 0, len, pt, 0, pt.Length));
+ }
+ }
+ else
+ {
+ if (data.Length >= 3)
+ {
+ buf[data[0].Trim()] = data[2].Trim();
+ }
+ else
+ {
+ buf[data[0].Trim()] = "";
+ }
+
+ }
+ }
}
}
+
+ private static void InitEngine(SparkleEngine sparkle, bool forEncryption)
+ {
+ int keySize = sparkle.GetKeyBytesSize();
+ int ivSize = sparkle.GetIVBytesSize();
+ int macSize = keySize * 8;
+
+ var parameters = new AeadParameters(new KeyParameter(new byte[keySize]), macSize, new byte[ivSize], null);
+ sparkle.Init(forEncryption, parameters);
+ }
}
}
|