diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2023-04-22 23:34:34 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2023-04-22 23:34:34 +0700 |
commit | 3306ab62405eeaa99189cd8fd0814bd8371c5fbf (patch) | |
tree | 37068af454fca4a3fd1f6cbbb6495b0fd1a4cf97 | |
parent | Add SparkleTest (diff) | |
download | BouncyCastle.NET-ed25519-3306ab62405eeaa99189cd8fd0814bd8371c5fbf.tar.xz |
SparkleDigest perf. opts.
-rw-r--r-- | crypto/src/crypto/digests/SparkleDigest.cs | 460 |
1 files changed, 342 insertions, 118 deletions
diff --git a/crypto/src/crypto/digests/SparkleDigest.cs b/crypto/src/crypto/digests/SparkleDigest.cs index 5cc438f88..b1be8aa82 100644 --- a/crypto/src/crypto/digests/SparkleDigest.cs +++ b/crypto/src/crypto/digests/SparkleDigest.cs @@ -1,5 +1,7 @@ using System; -using System.IO; +#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER +using System.Runtime.CompilerServices; +#endif using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; @@ -30,13 +32,14 @@ namespace Org.BouncyCastle.Crypto.Digests private string algorithmName; private readonly uint[] state; - private readonly MemoryStream message = new MemoryStream(); + private readonly byte[] m_buf = new byte[RATE_BYTES]; private readonly int DIGEST_BYTES; private readonly int SPARKLE_STEPS_SLIM; private readonly int SPARKLE_STEPS_BIG; - private readonly int STATE_BRANS; private readonly int STATE_UINTS; + private int m_bufPos = 0; + public SparkleDigest(SparkleParameters sparkleParameters) { switch (sparkleParameters) @@ -58,7 +61,7 @@ namespace Org.BouncyCastle.Crypto.Digests default: throw new ArgumentException("Invalid definition of SCHWAEMM instance"); } - STATE_BRANS = STATE_UINTS >> 1; + state = new uint[STATE_UINTS]; } @@ -70,20 +73,84 @@ namespace Org.BouncyCastle.Crypto.Digests public void Update(byte input) { - message.WriteByte(input); + if (m_bufPos == RATE_BYTES) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ProcessBlock(m_buf, SPARKLE_STEPS_SLIM); +#else + ProcessBlock(m_buf, 0, SPARKLE_STEPS_SLIM); +#endif + m_bufPos = 0; + } + + m_buf[m_bufPos++] = input; } public void BlockUpdate(byte[] input, int inOff, int inLen) { Check.DataLength(input, inOff, inLen, "input buffer too short"); - message.Write(input, inOff, inLen); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + BlockUpdate(input.AsSpan(inOff, inLen)); +#else + if (inLen < 1) + return; + + int available = RATE_BYTES - m_bufPos; + if (inLen <= available) + { + Array.Copy(input, inOff, m_buf, m_bufPos, inLen); + m_bufPos += inLen; + return; + } + + int inPos = 0; + if (m_bufPos > 0) + { + Array.Copy(input, inOff, m_buf, m_bufPos, available); + ProcessBlock(m_buf, 0, SPARKLE_STEPS_SLIM); + inPos += available; + } + + int remaining; + while ((remaining = inLen - inPos) > RATE_BYTES) + { + ProcessBlock(input, inOff + inPos, SPARKLE_STEPS_SLIM); + inPos += RATE_BYTES; + } + + Array.Copy(input, inOff + inPos, m_buf, 0, remaining); + m_bufPos = remaining; +#endif } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER public void BlockUpdate(ReadOnlySpan<byte> input) { - message.Write(input); + int available = RATE_BYTES - m_bufPos; + if (input.Length <= available) + { + input.CopyTo(m_buf.AsSpan(m_bufPos)); + m_bufPos += input.Length; + return; + } + + if (m_bufPos > 0) + { + input[..available].CopyTo(m_buf.AsSpan(m_bufPos)); + input = input[available..]; + + ProcessBlock(m_buf, SPARKLE_STEPS_SLIM); + } + + while (input.Length > RATE_BYTES) + { + ProcessBlock(input, SPARKLE_STEPS_SLIM); + input = input[RATE_BYTES..]; + } + + input.CopyTo(m_buf); + m_bufPos = input.Length; } #endif @@ -91,151 +158,308 @@ namespace Org.BouncyCastle.Crypto.Digests { Check.OutputLength(output, outOff, DIGEST_BYTES, "output buffer too short"); - byte[] input = message.GetBuffer(); - int inlen = (int)message.Length, i, inOff = 0; - uint tmpx, tmpy; - // Main Hashing Loop - uint[] in32 = Pack.LE_To_UInt32(input, 0, inlen >> 2); - while (inlen > RATE_BYTES) - { - // addition of a buffer block to the state - tmpx = 0; - tmpy = 0; - 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_UINTS; i += 2) - { - state[i] ^= (in32[i + (inOff >> 2)] ^ tmpy); - state[i + 1] ^= (in32[i + 1 + (inOff >> 2)] ^ tmpx); - } - for (i = RATE_UINTS; i < (STATE_UINTS / 2); i += 2) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return DoFinal(output.AsSpan(outOff)); +#else + // addition of constant M1 or M2 to the state + if (m_bufPos < RATE_BYTES) + { + state[(STATE_UINTS >> 1) - 1] ^= 1U << 24; + + // padding + m_buf[m_bufPos++] = 0x80; + while(++m_bufPos < RATE_BYTES) { - state[i] ^= tmpy; - state[i + 1] ^= tmpx; + m_buf[m_bufPos] = 0x00; } - // execute SPARKLE with slim number of steps - sparkle_opt(state, STATE_BRANS, SPARKLE_STEPS_SLIM); - inlen -= RATE_BYTES; - inOff += RATE_BYTES; } - // Hashing of Last Block - // addition of constant M1 or M2 to the state - state[STATE_BRANS - 1] ^= ((inlen < RATE_BYTES) ? (1u << 24) : (1u << 25)); + else + { + state[(STATE_UINTS >> 1) - 1] ^= 1U << 25; + } + // addition of last msg block (incl. padding) - uint[] buffer = new uint[RATE_UINTS]; - for (i = 0; i < inlen; ++i) + ProcessBlock(m_buf, 0, SPARKLE_STEPS_BIG); + + Pack.UInt32_To_LE(state, 0, RATE_UINTS, output, outOff); + + if (STATE_UINTS == 16) { - buffer[i >> 2] |= (input[inOff++] & 0xffu) << ((i & 3) << 3); + SparkleOpt16(state, SPARKLE_STEPS_SLIM); + Pack.UInt32_To_LE(state, 0, RATE_UINTS, output, outOff + 16); + SparkleOpt16(state, SPARKLE_STEPS_SLIM); + Pack.UInt32_To_LE(state, 0, RATE_UINTS, output, outOff + 32); } - if (inlen < RATE_BYTES) - { // padding - buffer[i >> 2] |= 0x80u << ((i & 3) << 3); + else + { + SparkleOpt12(state, SPARKLE_STEPS_SLIM); + Pack.UInt32_To_LE(state, 0, RATE_UINTS, output, outOff + 16); } - tmpx = 0; - tmpy = 0; - for (i = 0; i < RATE_UINTS; i += 2) + + Reset(); + return DIGEST_BYTES; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + // addition of constant M1 or M2 to the state + if (m_bufPos < RATE_BYTES) { - tmpx ^= buffer[i]; - tmpy ^= buffer[i + 1]; + state[(STATE_UINTS >> 1) - 1] ^= 1U << 24; + + // padding + m_buf[m_bufPos++] = 0x80; + while(++m_bufPos < RATE_BYTES) + { + m_buf[m_bufPos] = 0x00; + } } - tmpx = ELL(tmpx); - tmpy = ELL(tmpy); - for (i = 0; i < RATE_UINTS; i += 2) + else { - state[i] ^= (buffer[i] ^ tmpy); - state[i + 1] ^= (buffer[i + 1] ^ tmpx); + state[(STATE_UINTS >> 1) - 1] ^= 1U << 25; } - for (i = RATE_UINTS; i < (STATE_UINTS / 2); i += 2) + + // 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) { - state[i] ^= tmpy; - state[i + 1] ^= tmpx; + SparkleOpt16(state, SPARKLE_STEPS_SLIM); + Pack.UInt32_To_LE(state[..RATE_UINTS], output[16..]); + SparkleOpt16(state, SPARKLE_STEPS_SLIM); + Pack.UInt32_To_LE(state[..RATE_UINTS], output[32..]); } - // execute SPARKLE with big number of steps - sparkle_opt(state, STATE_BRANS, SPARKLE_STEPS_BIG); - Pack.UInt32_To_LE(state, 0, RATE_UINTS, output, outOff); - int outlen = RATE_BYTES; - outOff += RATE_BYTES; - while (outlen < DIGEST_BYTES) + else { - sparkle_opt(state, STATE_BRANS, SPARKLE_STEPS_SLIM); - Pack.UInt32_To_LE(state, 0, RATE_UINTS, output, outOff); - outlen += RATE_BYTES; - outOff += RATE_BYTES; + SparkleOpt12(state, SPARKLE_STEPS_SLIM); + Pack.UInt32_To_LE(state[..RATE_UINTS], output[16..]); } + Reset(); return DIGEST_BYTES; } +#endif -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - public int DoFinal(Span<byte> output) + public void Reset() { - byte[] rv = new byte[DIGEST_BYTES]; - DoFinal(rv, 0); - rv.AsSpan(0, rv.Length).CopyTo(output); - return DIGEST_BYTES; + Arrays.Fill(state, 0U); + Arrays.Fill(m_buf, 0x00); + m_bufPos = 0; } + +#if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void ProcessBlock(ReadOnlySpan<byte> block, int steps) + { + uint t0 = Pack.LE_To_UInt32(block); + uint t1 = Pack.LE_To_UInt32(block[4..]); + uint t2 = Pack.LE_To_UInt32(block[8..]); + uint t3 = Pack.LE_To_UInt32(block[12..]); +#else + private void ProcessBlock(byte[] buf, int off, int steps) + { + uint t0 = Pack.LE_To_UInt32(buf, off ); + uint t1 = Pack.LE_To_UInt32(buf, off + 4); + uint t2 = Pack.LE_To_UInt32(buf, off + 8); + uint t3 = Pack.LE_To_UInt32(buf, off + 12); #endif - public void Reset() + // 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[6] ^= ty; + state[7] ^= tx; + SparkleOpt16(state, steps); + } + else + { + SparkleOpt12(state, steps); + } + } + + private static void SparkleOpt12(uint[] state, int steps) { - message.SetLength(0); - Arrays.Fill(state, 0U); + 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 x0 = s00; + uint y0 = s01; + + uint tx = ELL(s00 ^ s02 ^ s04); + uint ty = ELL(s01 ^ s03 ^ s05); + + s00 = s08 ^ s02 ^ ty; + s01 = s09 ^ s03 ^ tx; + s08 = s02; + s09 = s03; + + s02 = s10 ^ s04 ^ ty; + s03 = s11 ^ s05 ^ tx; + s10 = s04; + s11 = s05; + + s04 = s06 ^ x0 ^ ty; + s05 = s07 ^ y0 ^ tx; + s06 = x0; + s07 = y0; + } + + 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 void sparkle_opt(uint[] state, int brans, int steps) + private static void SparkleOpt16(uint[] state, int steps) { - uint i, j, rc, tmpx, tmpy, x0, y0; - for (i = 0; i < steps; i++) + 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]; + + for (int i = 0; i < steps; ++i) { // Add round ant - state[1] ^= RCON[i & 7]; - state[3] ^= i; + s01 ^= RCON[i & 7]; + s03 ^= (uint)i; + // ARXBOX layer - for (j = 0; j < 2 * brans; j += 2) - { - rc = RCON[j >> 1]; - state[j] += Integers.RotateRight(state[j + 1], 31); - state[j + 1] ^= Integers.RotateRight(state[j], 24); - state[j] ^= rc; - state[j] += Integers.RotateRight(state[j + 1], 17); - state[j + 1] ^= Integers.RotateRight(state[j], 17); - state[j] ^= rc; - state[j] += state[j + 1]; - state[j + 1] ^= Integers.RotateRight(state[j], 31); - state[j] ^= rc; - state[j] += Integers.RotateRight(state[j + 1], 24); - state[j + 1] ^= Integers.RotateRight(state[j], 16); - state[j] ^= rc; - } + 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 - tmpx = x0 = state[0]; - tmpy = y0 = state[1]; - for (j = 2; j < brans; j += 2) - { - tmpx ^= state[j]; - tmpy ^= state[j + 1]; - } - tmpx = ELL(tmpx); - tmpy = ELL(tmpy); - for (j = 2; j < brans; j += 2) - { - state[j - 2] = state[j + brans] ^ state[j] ^ tmpy; - state[j + brans] = state[j]; - state[j - 1] = state[j + brans + 1] ^ state[j + 1] ^ tmpx; - state[j + brans + 1] = state[j + 1]; - } - state[brans - 2] = state[brans] ^ x0 ^ tmpy; - state[brans] = x0; - state[brans - 1] = state[brans + 1] ^ y0 ^ tmpx; - state[brans + 1] = y0; + uint x0 = s00; + uint y0 = s01; + + uint tx = ELL(s00 ^ s02 ^ s04 ^ s06); + uint ty = ELL(s01 ^ s03 ^ s05 ^ s07); + + s00 = s10 ^ s02 ^ ty; + s01 = s11 ^ s03 ^ tx; + s10 = s02; + s11 = s03; + + s02 = s12 ^ s04 ^ ty; + s03 = s13 ^ s05 ^ tx; + s12 = s04; + s13 = s05; + + s04 = s14 ^ s06 ^ ty; + s05 = s15 ^ s07 ^ tx; + s14 = s06; + s15 = s07; + + s06 = s08 ^ x0 ^ ty; + s07 = s09 ^ y0 ^ tx; + s08 = x0; + s09 = y0; } + + 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 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 + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif private static uint ELL(uint x) { return Integers.RotateRight(x ^ (x << 16), 16); |