From c091f2d5eba02afa4dd5bedc30c8003a4631b970 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 13 Feb 2023 16:00:19 +0700 Subject: Refactor LWC algorithms --- crypto/src/crypto/digests/AsconDigest.cs | 32 ++-- crypto/src/crypto/digests/ISAPDigest.cs | 17 +- crypto/src/crypto/digests/PhotonBeetleDigest.cs | 61 +++---- crypto/src/crypto/digests/SparkleDigest.cs | 169 +++++++++---------- crypto/src/crypto/digests/XoodyakDigest.cs | 211 ++++++++++++------------ crypto/src/crypto/engines/ElephantEngine.cs | 71 ++++---- crypto/src/crypto/engines/ISAPEngine.cs | 14 +- crypto/src/crypto/engines/PhotonBeetleEngine.cs | 44 ++--- crypto/src/crypto/engines/SparkleEngine.cs | 188 ++++++++++----------- crypto/src/crypto/engines/XoodyakEngine.cs | 95 ++++++----- crypto/test/src/crypto/test/ElephantTest.cs | 2 +- crypto/test/src/crypto/test/ISAPTest.cs | 2 +- crypto/test/src/crypto/test/PhotonBeetleTest.cs | 2 +- crypto/test/src/crypto/test/SparkleTest.cs | 2 +- crypto/test/src/crypto/test/XoodyakTest.cs | 2 +- 15 files changed, 423 insertions(+), 489 deletions(-) diff --git a/crypto/src/crypto/digests/AsconDigest.cs b/crypto/src/crypto/digests/AsconDigest.cs index 343036b99..20d39ce77 100644 --- a/crypto/src/crypto/digests/AsconDigest.cs +++ b/crypto/src/crypto/digests/AsconDigest.cs @@ -8,6 +8,11 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { + /// ASCON v1.2 Hash, https://ascon.iaik.tugraz.at/ . + /// + /// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/ascon-spec-final.pdf
+ /// ASCON v1.2 Hash with reference to C Reference Impl from: https://github.com/ascon/ascon-c . + ///
public sealed class AsconDigest : IDigest { @@ -247,15 +252,18 @@ namespace Org.BouncyCastle.Crypto.Digests private void P(int nr) { - if (nr == 12) + //if (nr >= 8) { - ROUND(0xf0UL); - ROUND(0xe1UL); - ROUND(0xd2UL); - ROUND(0xc3UL); + if (nr == 12) + { + ROUND(0xf0UL); + ROUND(0xe1UL); + ROUND(0xd2UL); + ROUND(0xc3UL); + } + ROUND(0xb4UL); + ROUND(0xa5UL); } - ROUND(0xb4UL); - ROUND(0xa5UL); ROUND(0x96UL); ROUND(0x87UL); ROUND(0x78UL); @@ -267,12 +275,12 @@ namespace Org.BouncyCastle.Crypto.Digests #if NETSTANDARD1_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif - private void ROUND(ulong C) + private void ROUND(ulong c) { - ulong t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C)); - ulong t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3)); - ulong t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4); - ulong t3 = x0 ^ x1 ^ x2 ^ C ^ (~x0 & (x3 ^ x4)); + ulong t0 = x0 ^ x1 ^ x2 ^ x3 ^ c ^ (x1 & (x0 ^ x2 ^ x4 ^ c)); + ulong t1 = x0 ^ x2 ^ x3 ^ x4 ^ c ^ ((x1 ^ x2 ^ c) & (x1 ^ x3)); + ulong t2 = x1 ^ x2 ^ x4 ^ c ^ (x3 & x4); + ulong t3 = x0 ^ x1 ^ x2 ^ c ^ (~x0 & (x3 ^ x4)); ulong t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1); x0 = t0 ^ Longs.RotateRight(t0, 19) ^ Longs.RotateRight(t0, 28); x1 = t1 ^ Longs.RotateRight(t1, 39) ^ Longs.RotateRight(t1, 61); diff --git a/crypto/src/crypto/digests/ISAPDigest.cs b/crypto/src/crypto/digests/ISAPDigest.cs index 348242e6e..83214fe6b 100644 --- a/crypto/src/crypto/digests/ISAPDigest.cs +++ b/crypto/src/crypto/digests/ISAPDigest.cs @@ -12,9 +12,8 @@ namespace Org.BouncyCastle.Crypto.Digests public sealed class IsapDigest : IDigest { + private readonly MemoryStream buffer = new MemoryStream(); private ulong x0, x1, x2, x3, x4; - private ulong t0, t1, t2, t3, t4; - private MemoryStream buffer = new MemoryStream(); public string AlgorithmName => "ISAP Hash"; @@ -48,8 +47,6 @@ namespace Org.BouncyCastle.Crypto.Digests #else Check.OutputLength(output, outOff, 32, "output buffer is too short"); - t0 = t1 = t2 = t3 = t4 = 0; - /* init state */ x0 = 17191252062196199485UL; x1 = 10066134719181819906UL; @@ -89,8 +86,6 @@ namespace Org.BouncyCastle.Crypto.Digests { Check.OutputLength(output, 32, "output buffer is too short"); - t0 = t1 = t2 = t3 = t4 = 0; - /* init state */ x0 = 17191252062196199485UL; x1 = 10066134719181819906UL; @@ -150,11 +145,11 @@ namespace Org.BouncyCastle.Crypto.Digests #endif private void ROUND(ulong C) { - t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C)); - t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3)); - t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4); - t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4)); - t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1); + ulong t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C)); + ulong t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3)); + ulong t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4); + ulong t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4)); + ulong t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1); x0 = t0 ^ Longs.RotateRight(t0, 19) ^ Longs.RotateRight(t0, 28); x1 = t1 ^ Longs.RotateRight(t1, 39) ^ Longs.RotateRight(t1, 61); x2 = ~(t2 ^ Longs.RotateRight(t2, 1) ^ Longs.RotateRight(t2, 6)); diff --git a/crypto/src/crypto/digests/PhotonBeetleDigest.cs b/crypto/src/crypto/digests/PhotonBeetleDigest.cs index 1af8aee2c..c7cbfdfca 100644 --- a/crypto/src/crypto/digests/PhotonBeetleDigest.cs +++ b/crypto/src/crypto/digests/PhotonBeetleDigest.cs @@ -12,7 +12,7 @@ namespace Org.BouncyCastle.Crypto.Digests * Photon-Beetle with reference to C Reference Impl from: https://github.com/PHOTON-Beetle/Software *

*/ - public class PhotonBeetleDigest + public sealed class PhotonBeetleDigest : IDigest { private byte[] state; @@ -64,7 +64,6 @@ namespace Org.BouncyCastle.Crypto.Digests } } - public String AlgorithmName => "Photon-Beetle Hash"; public int GetDigestSize() => TAG_INBYTES; @@ -80,22 +79,24 @@ namespace Org.BouncyCastle.Crypto.Digests buffer.WriteByte(input); } - public void BlockUpdate(byte[] input, int inOff, int len) + public void BlockUpdate(byte[] input, int inOff, int inLen) { - if ((inOff + len) > input.Length) - { - throw new DataLengthException("input buffer too short"); - } - buffer.Write(input, inOff, len); + Check.DataLength(input, inOff, inLen, "input buffer too short"); + + buffer.Write(input, inOff, inLen); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan input) + { + buffer.Write(input); + } +#endif public int DoFinal(byte[] output, int outOff) { - if (32 + outOff > output.Length) - { - throw new OutputLengthException("output buffer is too short"); - } + Check.OutputLength(output, outOff, 32, "output buffer is too short"); + byte[] input = buffer.GetBuffer(); int inlen = (int)buffer.Length; if (inlen == 0) @@ -120,11 +121,11 @@ namespace Org.BouncyCastle.Crypto.Digests for (i = 0; i < Dlen_inblocks - 1; i++) { PHOTON_Permutation(); - XOR(input, INITIAL_RATE_INBYTES + i * RATE_INBYTES, RATE_INBYTES); + Bytes.XorTo(RATE_INBYTES, input, INITIAL_RATE_INBYTES + i * RATE_INBYTES, state, 0); } PHOTON_Permutation(); LastDBlocklen = inlen - i * RATE_INBYTES; - XOR(input, INITIAL_RATE_INBYTES + i * RATE_INBYTES, LastDBlocklen); + Bytes.XorTo(LastDBlocklen, input, INITIAL_RATE_INBYTES + i * RATE_INBYTES, state, 0); if (LastDBlocklen < RATE_INBYTES) { state[LastDBlocklen] ^= 0x01; // ozs @@ -138,14 +139,15 @@ namespace Org.BouncyCastle.Crypto.Digests return TAG_INBYTES; } - void XOR(byte[] in_right, int rOff, int iolen_inbytes) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span output) { - for (int i = 0; i < iolen_inbytes; i++) - { - state[i] ^= in_right[i + rOff]; - } + byte[] rv = new byte[32]; + int rlt = DoFinal(rv, 0); + rv.AsSpan(0, 32).CopyTo(output); + return rlt; } - +#endif public void Reset() { @@ -153,7 +155,7 @@ namespace Org.BouncyCastle.Crypto.Digests Arrays.Fill(state, (byte)0); } - void PHOTON_Permutation() + private void PHOTON_Permutation() { int i, j, k, l; for (i = 0; i < DSquare; i++) @@ -222,22 +224,5 @@ namespace Org.BouncyCastle.Crypto.Digests state[i >> 1] = (byte)(((state_2d[i >> Dq][i & Dr] & 0xf)) | ((state_2d[i >> Dq][(i + 1) & Dr] & 0xf) << 4)); } } - -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - - public int DoFinal(Span output) - { - byte[] rv = new byte[32]; - int rlt = DoFinal(rv, 0); - rv.AsSpan(0, 32).CopyTo(output); - return rlt; - } - - public void BlockUpdate(ReadOnlySpan input) - { - buffer.Write(input.ToArray(), 0, input.Length); - } - -#endif } } diff --git a/crypto/src/crypto/digests/SparkleDigest.cs b/crypto/src/crypto/digests/SparkleDigest.cs index ad558effc..bd03fdf0d 100644 --- a/crypto/src/crypto/digests/SparkleDigest.cs +++ b/crypto/src/crypto/digests/SparkleDigest.cs @@ -12,7 +12,7 @@ namespace Org.BouncyCastle.Crypto.Digests /// Specification: /// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/sparkle-spec-final.pdf . /// - public class SparkleDigest + public sealed class SparkleDigest : IDigest { public enum SparkleParameters @@ -21,9 +21,12 @@ namespace Org.BouncyCastle.Crypto.Digests ESCH384 } + private static readonly uint[] RCON = { 0xB7E15162U, 0xBF715880U, 0x38B4DA56U, 0x324E7738U, 0xBB1185EBU, + 0x4F7C7B57U, 0xCFBFA1C8U, 0xC2B3293DU }; + private string algorithmName; private readonly uint[] state; - private MemoryStream message = new MemoryStream(); + private readonly MemoryStream message = new MemoryStream(); private readonly int DIGEST_BYTES; private readonly int SPARKLE_STEPS_SLIM; private readonly int SPARKLE_STEPS_BIG; @@ -64,85 +67,35 @@ namespace Org.BouncyCastle.Crypto.Digests state = new uint[STATE_WORDS]; } - private uint ELL(uint x) - { - return Integers.RotateRight(x ^ (x << 16), 16); - } + public string AlgorithmName => algorithmName; - private static readonly uint[] RCON = {0xB7E15162, 0xBF715880, 0x38B4DA56, 0x324E7738, 0xBB1185EB, 0x4F7C7B57, - 0xCFBFA1C8, 0xC2B3293D}; + public int GetDigestSize() => DIGEST_BYTES; - void sparkle_opt(uint[] state, int brans, int steps) + public int GetByteLength() => RATE_BYTES; + + public void Update(byte input) { - uint i, j, rc, tmpx, tmpy, x0, y0; - for (i = 0; i < steps; i++) - { - // Add round ant - state[1] ^= RCON[i & 7]; - state[3] ^= 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; - } - // 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; - } + message.WriteByte(input); } - public int GetDigestSize() + public void BlockUpdate(byte[] input, int inOff, int inLen) { - return DIGEST_BYTES; - } + Check.DataLength(input, inOff, inLen, "input buffer too short"); + message.Write(input, inOff, inLen); + } - public void BlockUpdate(byte[] input, int inOff, int len) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan input) { - if (inOff + len > input.Length) - { - throw new DataLengthException(algorithmName + " input buffer too short"); - } - message.Write(input, inOff, len); + message.Write(input); } - +#endif public int DoFinal(byte[] output, int outOff) { - if (outOff + DIGEST_BYTES > output.Length) - { - throw new OutputLengthException(algorithmName + " input buffer too short"); - } + 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; @@ -220,40 +173,80 @@ namespace Org.BouncyCastle.Crypto.Digests outOff += RATE_BYTES; } return DIGEST_BYTES; - } - public string AlgorithmName => algorithmName; + // TODO Reset? + } - public void Update(byte input) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span output) { - message.Write(new byte[] { input }, 0, 1); + 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; } +#endif public void Reset() { message.SetLength(0); - Arrays.Fill(state, (byte)0); + Arrays.Fill(state, 0U); } - public int GetByteLength() + private void sparkle_opt(uint[] state, int brans, int steps) { - return RATE_BYTES; - } - -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - public void BlockUpdate(ReadOnlySpan input) - { - message.Write(input); + uint i, j, rc, tmpx, tmpy, x0, y0; + for (i = 0; i < steps; i++) + { + // Add round ant + state[1] ^= RCON[i & 7]; + state[3] ^= 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; + } + // 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; + } } - public int DoFinal(Span output) + private static uint ELL(uint x) { - byte[] rv=new byte[DIGEST_BYTES]; - DoFinal(rv, 0); - Reset(); - rv.AsSpan(0, rv.Length).CopyTo(output); - return DIGEST_BYTES; + return Integers.RotateRight(x ^ (x << 16), 16); } -#endif } } diff --git a/crypto/src/crypto/digests/XoodyakDigest.cs b/crypto/src/crypto/digests/XoodyakDigest.cs index cf1afcc10..4836f4d2b 100644 --- a/crypto/src/crypto/digests/XoodyakDigest.cs +++ b/crypto/src/crypto/digests/XoodyakDigest.cs @@ -1,11 +1,25 @@ using System; using System.IO; + using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { - public class XoodyakDigest : IDigest + public sealed class XoodyakDigest + : IDigest { + private enum MODE + { + ModeHash, + ModeKeyed + } + + private const int Rkin = 44; + + private static readonly uint[] RC = { 0x00000058U, 0x00000038U, 0x000003C0U, 0x000000D0U, 0x00000120U, + 0x00000014U, 0x00000060U, 0x0000002CU, 0x00000380U, 0x000000F0U, 0x000001A0U, 0x00000012U }; + private byte[] state; private int phase; private MODE mode; @@ -16,19 +30,11 @@ namespace Org.BouncyCastle.Crypto.Digests private const int PhaseUp = 2; private const int NLANES = 12; private const int NROWS = 3; - private const int NCOLUMS = 4; + private const int NCOLUMNS = 4; private const int MAXROUNDS = 12; private const int TAGLEN = 16; private const int Rhash = 16; - const int Rkin = 44; - private readonly uint[] RC = {0x00000058, 0x00000038, 0x000003C0, 0x000000D0, 0x00000120, 0x00000014, 0x00000060, - 0x0000002C, 0x00000380, 0x000000F0, 0x000001A0, 0x00000012}; - private MemoryStream buffer = new MemoryStream(); - enum MODE - { - ModeHash, - ModeKeyed - } + private readonly MemoryStream buffer = new MemoryStream(); public XoodyakDigest() { @@ -38,6 +44,78 @@ namespace Org.BouncyCastle.Crypto.Digests public string AlgorithmName => "Xoodyak Hash"; + public int GetDigestSize() => 32; + + public int GetByteLength() => Rabsorb; + + public void Update(byte input) + { + buffer.WriteByte(input); + } + + public void BlockUpdate(byte[] input, int inOff, int inLen) + { + Check.DataLength(input, inOff, inLen, "input buffer too short"); + + buffer.Write(input, inOff, inLen); + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan input) + { + buffer.Write(input); + } +#endif + + public int DoFinal(byte[] output, int outOff) + { + Check.OutputLength(output, outOff, 32, "output buffer is too short"); + + byte[] input = buffer.GetBuffer(); + int inLen = (int)buffer.Length; + int inOff = 0; + uint Cd = 0x03; + int splitLen; + do + { + if (phase != PhaseUp) + { + Up(null, 0, 0, 0); + } + splitLen = System.Math.Min(inLen, Rabsorb); + Down(input, inOff, splitLen, Cd); + Cd = 0; + inOff += splitLen; + inLen -= splitLen; + } + while (inLen != 0); + Up(output, outOff, TAGLEN, 0x40); + Down(null, 0, 0, 0); + Up(output, outOff + TAGLEN, TAGLEN, 0); + return 32; + + // TODO Reset? + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span output) + { + byte[] rv = new byte[32]; + int rlt = DoFinal(rv, 0); + rv.AsSpan(0, 32).CopyTo(output); + return rlt; + } +#endif + + public void Reset() + { + Array.Clear(state, 0, state.Length); + phase = PhaseUp; + mode = MODE.ModeHash; + Rabsorb = Rhash; + buffer.SetLength(0); + } + private void Up(byte[] Yi, int YiOff, int YiLen, uint Cu) { if (mode != MODE.ModeHash) @@ -48,21 +126,21 @@ namespace Org.BouncyCastle.Crypto.Digests Pack.LE_To_UInt32(state, 0, a, 0, a.Length); uint x, y; uint[] b = new uint[NLANES]; - uint[] p = new uint[NCOLUMS]; - uint[] e = new uint[NCOLUMS]; + uint[] p = new uint[NCOLUMNS]; + uint[] e = new uint[NCOLUMNS]; for (int i = 0; i < MAXROUNDS; ++i) { /* Theta: Column Parity Mixer */ - for (x = 0; x < NCOLUMS; ++x) + for (x = 0; x < NCOLUMNS; ++x) { p[x] = a[index(x, 0)] ^ a[index(x, 1)] ^ a[index(x, 2)]; } - for (x = 0; x < NCOLUMS; ++x) + for (x = 0; x < NCOLUMNS; ++x) { y = p[(x + 3) & 3]; - e[x] = ROTL32(y, 5) ^ ROTL32(y, 14); + e[x] = Integers.RotateLeft(y, 5) ^ Integers.RotateLeft(y, 14); } - for (x = 0; x < NCOLUMS; ++x) + for (x = 0; x < NCOLUMNS; ++x) { for (y = 0; y < NROWS; ++y) { @@ -70,16 +148,16 @@ namespace Org.BouncyCastle.Crypto.Digests } } /* Rho-west: plane shift */ - for (x = 0; x < NCOLUMS; ++x) + for (x = 0; x < NCOLUMNS; ++x) { b[index(x, 0)] = a[index(x, 0)]; b[index(x, 1)] = a[index(x + 3, 1)]; - b[index(x, 2)] = ROTL32(a[index(x, 2)], 11); + b[index(x, 2)] = Integers.RotateLeft(a[index(x, 2)], 11); } /* Iota: round ant */ b[0] ^= RC[i]; /* Chi: non linear layer */ - for (x = 0; x < NCOLUMS; ++x) + for (x = 0; x < NCOLUMNS; ++x) { for (y = 0; y < NROWS; ++y) { @@ -87,11 +165,11 @@ namespace Org.BouncyCastle.Crypto.Digests } } /* Rho-east: plane shift */ - for (x = 0; x < NCOLUMS; ++x) + for (x = 0; x < NCOLUMNS; ++x) { b[index(x, 0)] = a[index(x, 0)]; - b[index(x, 1)] = ROTL32(a[index(x, 1)], 1); - b[index(x, 2)] = ROTL32(a[index(x + 2, 2)], 8); + b[index(x, 1)] = Integers.RotateLeft(a[index(x, 1)], 1); + b[index(x, 2)] = Integers.RotateLeft(a[index(x + 2, 2)], 8); } Array.Copy(b, 0, a, 0, NLANES); } @@ -116,92 +194,7 @@ namespace Org.BouncyCastle.Crypto.Digests private uint index(uint x, uint y) { - return (((y % NROWS) * NCOLUMS) + ((x) % NCOLUMS)); - } - - private uint ROTL32(uint a, int offset) - { - return (a << (offset & 31)) ^ (a >> ((32 - (offset)) & 31)); - } - - public void BlockUpdate(byte[] input, int inOff, int inLen) - { - if (inOff + inLen > input.Length) - { - throw new DataLengthException("input buffer too short"); - } - buffer.Write(input, inOff, inLen); - } - - public int DoFinal(byte[] output, int outOff) - { - if (32 + outOff > output.Length) - { - throw new OutputLengthException("output buffer is too short"); - } - byte[] input = buffer.GetBuffer(); - int inLen = (int)buffer.Length; - int inOff = 0; - uint Cd = 0x03; - int splitLen; - do - { - if (phase != PhaseUp) - { - Up(null, 0, 0, 0); - } - splitLen = System.Math.Min(inLen, Rabsorb); - Down(input, inOff, splitLen, Cd); - Cd = 0; - inOff += splitLen; - inLen -= splitLen; - } - while (inLen != 0); - Up(output, outOff, TAGLEN, 0x40); - Down(null, 0, 0, 0); - Up(output, outOff + TAGLEN, TAGLEN, 0); - return 32; - } - - public int GetByteLength() - { - throw new NotImplementedException(); - } - - public int GetDigestSize() - { - return 32; + return (((y % NROWS) * NCOLUMNS) + ((x) % NCOLUMNS)); } - - public void Reset() - { - for (int i = 0; i < state.Length; ++i) - { - state[i] = 0; - } - phase = PhaseUp; - mode = MODE.ModeHash; - Rabsorb = Rhash; - buffer.SetLength(0); - } - - public void Update(byte input) - { - buffer.Write(new byte[] { input }, 0, 1); - } -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - public int DoFinal(Span output) - { - byte[] rv = new byte[32]; - int rlt = DoFinal(rv, 0); - rv.AsSpan(0, 32).CopyTo(output); - return rlt; - } - - public void BlockUpdate(ReadOnlySpan input) - { - buffer.Write(input.ToArray(), 0, input.Length); - } -#endif } } diff --git a/crypto/src/crypto/engines/ElephantEngine.cs b/crypto/src/crypto/engines/ElephantEngine.cs index 4e0e3216d..89d2ca6be 100644 --- a/crypto/src/crypto/engines/ElephantEngine.cs +++ b/crypto/src/crypto/engines/ElephantEngine.cs @@ -1,8 +1,10 @@ using System; +using System.Drawing; using System.IO; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math.Raw; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Engines @@ -13,6 +15,7 @@ namespace Org.BouncyCastle.Crypto.Engines * Specification: https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/elephant-spec-final.pdf */ public class ElephantEngine + // TODO IAeadCipher only : IAeadBlockCipher { public enum ElephantParameters @@ -22,7 +25,7 @@ namespace Org.BouncyCastle.Crypto.Engines elephant200 } - private bool forEncryption; + private bool forEncryption = true; // Safe output sizes before initialization private readonly string algorithmName; private ElephantParameters parameters; private int BLOCK_SIZE; @@ -226,28 +229,20 @@ namespace Org.BouncyCastle.Crypto.Engines { switch (parameters) { - case ElephantParameters.elephant160: - output[BLOCK_SIZE - 1] = (byte)((((input[0] & 0xFF) << 3) | ((input[0] & 0xFF) >> 5)) ^ - ((input[3] & 0xFF) << 7) ^ ((input[13] & 0xFF) >> 7)); - break; - case ElephantParameters.elephant176: - output[BLOCK_SIZE - 1] = (byte)(rotl(input[0]) ^ ((input[3] & 0xFF) << 7) ^ ((input[19] & 0xFF) >> 7)); - break; - case ElephantParameters.elephant200: - output[BLOCK_SIZE - 1] = (byte)(rotl(input[0]) ^ rotl(input[2]) ^ (input[13] << 1)); - break; + case ElephantParameters.elephant160: + output[BLOCK_SIZE - 1] = (byte)((((input[0] & 0xFF) << 3) | ((input[0] & 0xFF) >> 5)) ^ + ((input[3] & 0xFF) << 7) ^ ((input[13] & 0xFF) >> 7)); + break; + case ElephantParameters.elephant176: + output[BLOCK_SIZE - 1] = (byte)(rotl(input[0]) ^ ((input[3] & 0xFF) << 7) ^ ((input[19] & 0xFF) >> 7)); + break; + case ElephantParameters.elephant200: + output[BLOCK_SIZE - 1] = (byte)(rotl(input[0]) ^ rotl(input[2]) ^ (input[13] << 1)); + break; } Array.Copy(input, 1, output, 0, BLOCK_SIZE - 1); } - private void xor_block(byte[] state, byte[] block, int bOff, int size) - { - for (int i = 0; i < size; ++i) - { - state[i] ^= block[i + bOff]; - } - } - // Write the ith assocated data block to "output". // The nonce is prepended and padding is added as required. // adlen is the length of the associated data in bytes @@ -412,14 +407,13 @@ namespace Org.BouncyCastle.Crypto.Engines // Compute ciphertext block Array.Copy(npub, 0, buffer, 0, CRYPTO_NPUBBYTES); Arrays.Fill(buffer, CRYPTO_NPUBBYTES, BLOCK_SIZE, (byte)0); - xor_block(buffer, current_mask, 0, BLOCK_SIZE); - xor_block(buffer, next_mask, 0, BLOCK_SIZE); + Bytes.XorTo(BLOCK_SIZE, current_mask, buffer); + Bytes.XorTo(BLOCK_SIZE, next_mask, buffer); permutation(buffer); - xor_block(buffer, current_mask, 0, BLOCK_SIZE); - xor_block(buffer, next_mask, 0, BLOCK_SIZE); + Bytes.XorTo(BLOCK_SIZE, current_mask, buffer); + Bytes.XorTo(BLOCK_SIZE, next_mask, buffer); int r_size = (i == nblocks_m - 1) ? mlen - offset : BLOCK_SIZE; - xor_block(buffer, m, offset, r_size); - Array.Copy(buffer, 0, output, offset + outOff, r_size); + Bytes.Xor(r_size, m, offset, buffer, 0, output, outOff + offset); } if (i > 0 && i <= nblocks_c) { @@ -432,21 +426,21 @@ namespace Org.BouncyCastle.Crypto.Engines { get_c_block(buffer, m, 0, mlen, i - 1); } - xor_block(buffer, previous_mask, 0, BLOCK_SIZE); - xor_block(buffer, next_mask, 0, BLOCK_SIZE); + Bytes.XorTo(BLOCK_SIZE, previous_mask, buffer); + Bytes.XorTo(BLOCK_SIZE, next_mask, buffer); permutation(buffer); - xor_block(buffer, previous_mask, 0, BLOCK_SIZE); - xor_block(buffer, next_mask, 0, BLOCK_SIZE); - xor_block(tag_buffer, buffer, 0, BLOCK_SIZE); + Bytes.XorTo(BLOCK_SIZE, previous_mask, buffer); + Bytes.XorTo(BLOCK_SIZE, next_mask, buffer); + Bytes.XorTo(BLOCK_SIZE, buffer, tag_buffer); } // If there is any AD left, compute tag for AD block if (i + 1 < nblocks_ad) { get_ad_block(buffer, ad, adlen, npub, i + 1); - xor_block(buffer, next_mask, 0, BLOCK_SIZE); + Bytes.XorTo(BLOCK_SIZE, next_mask, buffer); permutation(buffer); - xor_block(buffer, next_mask, 0, BLOCK_SIZE); - xor_block(tag_buffer, buffer, 0, BLOCK_SIZE); + Bytes.XorTo(BLOCK_SIZE, next_mask, buffer); + Bytes.XorTo(BLOCK_SIZE, buffer, tag_buffer); } // Cyclically shift the mask buffers // Value of next_mask will be computed in the next iteration @@ -458,9 +452,9 @@ namespace Org.BouncyCastle.Crypto.Engines } outOff += mlen; tag = new byte[CRYPTO_ABYTES]; - xor_block(tag_buffer, expanded_key, 0, BLOCK_SIZE); + Bytes.XorTo(BLOCK_SIZE, expanded_key, tag_buffer); permutation(tag_buffer); - xor_block(tag_buffer, expanded_key, 0, BLOCK_SIZE); + Bytes.XorTo(BLOCK_SIZE, expanded_key, tag_buffer); if (forEncryption) { Array.Copy(tag_buffer, 0, tag, 0, CRYPTO_ABYTES); @@ -483,11 +477,13 @@ namespace Org.BouncyCastle.Crypto.Engines public int GetUpdateOutputSize(int len) { + // TODO return len; } public int GetOutputSize(int len) { + // TODO return len + CRYPTO_ABYTES; } @@ -514,13 +510,13 @@ namespace Org.BouncyCastle.Crypto.Engines public int ProcessByte(byte input, Span output) { - message.Write(new byte[]{ input }); + message.WriteByte(input); return 0; } public int ProcessBytes(ReadOnlySpan input, Span output) { - message.Write(input.ToArray()); + message.Write(input); return 0; } @@ -558,4 +554,3 @@ namespace Org.BouncyCastle.Crypto.Engines } } } - diff --git a/crypto/src/crypto/engines/ISAPEngine.cs b/crypto/src/crypto/engines/ISAPEngine.cs index 7ef629d1b..17c679439 100644 --- a/crypto/src/crypto/engines/ISAPEngine.cs +++ b/crypto/src/crypto/engines/ISAPEngine.cs @@ -35,7 +35,7 @@ namespace Org.BouncyCastle.Crypto.Engines private const int ISAP_STATE_SZ = 40; private string algorithmName; - private bool forEncryption; + private bool forEncryption = true; // Safe output sizes before initialization private bool initialised; private byte[] mac; private MemoryStream aadData = new MemoryStream(); @@ -112,7 +112,7 @@ namespace Org.BouncyCastle.Crypto.Engines protected ulong ISAP_IV1_64; protected ulong ISAP_IV2_64; protected ulong ISAP_IV3_64; - protected ulong x0, x1, x2, x3, x4, t0, t1, t2, t3, t4; + protected ulong x0, x1, x2, x3, x4; public override void init(byte[] k, byte[] npub, int ISAP_rH, int ISAP_rH_SZ) { @@ -248,11 +248,11 @@ namespace Org.BouncyCastle.Crypto.Engines #endif protected void ROUND(ulong C) { - t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C)); - t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3)); - t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4); - t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4)); - t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1); + ulong t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C)); + ulong t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3)); + ulong t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4); + ulong t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4)); + ulong t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1); x0 = t0 ^ Longs.RotateRight(t0, 19) ^ Longs.RotateRight(t0, 28); x1 = t1 ^ Longs.RotateRight(t1, 39) ^ Longs.RotateRight(t1, 61); x2 = ~(t2 ^ Longs.RotateRight(t2, 1) ^ Longs.RotateRight(t2, 6)); diff --git a/crypto/src/crypto/engines/PhotonBeetleEngine.cs b/crypto/src/crypto/engines/PhotonBeetleEngine.cs index e593bb6f5..5d96213a1 100644 --- a/crypto/src/crypto/engines/PhotonBeetleEngine.cs +++ b/crypto/src/crypto/engines/PhotonBeetleEngine.cs @@ -14,7 +14,8 @@ namespace Org.BouncyCastle.Crypto.Engines * Photon-Beetle with reference to C Reference Impl from: https://github.com/PHOTON-Beetle/Software *

*/ - public class PhotonBeetleEngine + public sealed class PhotonBeetleEngine + // TODO IAeadCipher only : IAeadBlockCipher { public enum PhotonBeetleParameters @@ -24,7 +25,7 @@ namespace Org.BouncyCastle.Crypto.Engines } private bool input_empty; - private bool forEncryption; + private bool forEncryption = true; // Safe output sizes before initialization private bool initialised; private byte[] K; private byte[] N; @@ -94,6 +95,7 @@ namespace Org.BouncyCastle.Crypto.Engines public string AlgorithmName => "Photon-Beetle AEAD"; + // TODO public IBlockCipher UnderlyingCipher => throw new NotImplementedException(); public byte[] GetMac() @@ -103,37 +105,33 @@ namespace Org.BouncyCastle.Crypto.Engines public int GetOutputSize(int len) { + // TODO return len + TAG_INBYTES; } public int GetUpdateOutputSize(int len) { + // TODO return len; } public void Init(bool forEncryption, ICipherParameters parameters) { this.forEncryption = forEncryption; - if (!(parameters is ParametersWithIV param)) - { + + if (!(parameters is ParametersWithIV ivParams)) throw new ArgumentException("Photon-Beetle AEAD init parameters must include an IV"); - } - ParametersWithIV ivParams = param; + N = ivParams.GetIV(); if (N == null || N.Length != CRYPTO_NPUBBYTES) - { throw new ArgumentException("Photon-Beetle AEAD requires exactly 16 bytes of IV"); - } - if (!(ivParams.Parameters is KeyParameter)) - { + + if (!(ivParams.Parameters is KeyParameter key)) throw new ArgumentException("Photon-Beetle AEAD init parameters must include a key"); - } - KeyParameter key = (KeyParameter)ivParams.Parameters; + K = key.GetKey(); if (K.Length != CRYPTO_KEYBYTES) - { throw new ArgumentException("Photon-Beetle AEAD key must be 128 bits long"); - } state = new byte[STATE_INBYTES]; state_2d = new byte[D][]; @@ -148,7 +146,7 @@ namespace Org.BouncyCastle.Crypto.Engines public void ProcessAadByte(byte input) { - aadData.Write(new byte[] { input }, 0, 1); + aadData.WriteByte(input); } public void ProcessAadBytes(byte[] inBytes, int inOff, int len) @@ -300,19 +298,11 @@ namespace Org.BouncyCastle.Crypto.Engines } if (forEncryption) { - XOR(plaintext, inOff, DBlen_inbytes); + Bytes.XorTo(DBlen_inbytes, plaintext, inOff, state, 0); } else { - XOR(ciphertext, inOff, DBlen_inbytes); - } - } - - void XOR(byte[] in_right, int rOff, int iolen_inbytes) - { - for (int i = 0; i < iolen_inbytes; i++) - { - state[i] ^= in_right[rOff++]; + Bytes.XorTo(DBlen_inbytes, ciphertext, inOff, state, 0); } } @@ -344,11 +334,11 @@ namespace Org.BouncyCastle.Crypto.Engines for (i = 0; i < Dlen_inblocks - 1; i++) { PHOTON_Permutation(); - XOR(A, i * RATE_INBYTES, RATE_INBYTES); + Bytes.XorTo(RATE_INBYTES, A, i * RATE_INBYTES, state, 0); } PHOTON_Permutation(); LastDBlocklen = adlen - i * RATE_INBYTES; - XOR(A, i * RATE_INBYTES, LastDBlocklen); + Bytes.XorTo(LastDBlocklen, A, i * RATE_INBYTES, state, 0); if (LastDBlocklen < RATE_INBYTES) { state[LastDBlocklen] ^= 0x01; // ozs diff --git a/crypto/src/crypto/engines/SparkleEngine.cs b/crypto/src/crypto/engines/SparkleEngine.cs index 63e400d4c..7d8c28f8e 100644 --- a/crypto/src/crypto/engines/SparkleEngine.cs +++ b/crypto/src/crypto/engines/SparkleEngine.cs @@ -14,7 +14,8 @@ namespace Org.BouncyCastle.Crypto.Engines /// Specification: /// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/sparkle-spec-final.pdf . /// - public class SparkleEngine + public sealed class SparkleEngine + // TODO IAeadCipher only : IAeadBlockCipher { public enum SparkleParameters @@ -24,8 +25,9 @@ namespace Org.BouncyCastle.Crypto.Engines SCHWAEMM192_192, SCHWAEMM256_256 } + private string algorithmName; - private bool forEncryption; + private bool forEncryption = true; // Safe output sizes before initialization private readonly uint[] state; private readonly uint[] k; private readonly uint[] npub; @@ -60,48 +62,48 @@ namespace Org.BouncyCastle.Crypto.Engines 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"); + 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; @@ -124,14 +126,9 @@ namespace Org.BouncyCastle.Crypto.Engines initialised = false; } - private uint ROT(uint x, int n) - { - return (((x) >> n) | ((x) << (32 - n))); - } - private uint ELL(uint x) { - return ROT(((x) ^ ((x) << 16)), 16); + return Integers.RotateRight(x ^ (x << 16), 16); } private static readonly uint[] RCON = {0xB7E15162, 0xBF715880, 0x38B4DA56, 0x324E7738, 0xBB1185EB, 0x4F7C7B57, @@ -149,17 +146,17 @@ namespace Org.BouncyCastle.Crypto.Engines for (j = 0; j < 2 * brans; j += 2) { rc = RCON[j >> 1]; - state[j] += ROT(state[j + 1], 31); - state[j + 1] ^= ROT(state[j], 24); + state[j] += Integers.RotateRight(state[j + 1], 31); + state[j + 1] ^= Integers.RotateRight(state[j], 24); state[j] ^= rc; - state[j] += ROT(state[j + 1], 17); - state[j + 1] ^= ROT(state[j], 17); + 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] ^= ROT(state[j], 31); + state[j + 1] ^= Integers.RotateRight(state[j], 31); state[j] ^= rc; - state[j] += ROT(state[j + 1], 24); - state[j + 1] ^= ROT(state[j], 16); + state[j] += Integers.RotateRight(state[j + 1], 24); + state[j + 1] ^= Integers.RotateRight(state[j], 16); state[j] ^= rc; } // Linear layer @@ -308,31 +305,22 @@ namespace Org.BouncyCastle.Crypto.Engines public void Init(bool forEncryption, ICipherParameters param) { this.forEncryption = forEncryption; - if (!(param is ParametersWithIV)) - { + if (!(param is ParametersWithIV ivParams)) throw new ArgumentException(algorithmName + " init parameters must include an IV"); - } - ParametersWithIV ivParams = (ParametersWithIV)param; byte[] iv = ivParams.GetIV(); - if (iv == null || iv.Length != SCHWAEMM_NONCE_LEN >> 3) - { throw new ArgumentException(algorithmName + " requires exactly 16 bytes of IV"); - } + Pack.LE_To_UInt32(iv, 0, npub, 0, RATE_WORDS); - if (!(ivParams.Parameters is KeyParameter)) - { + if (!(ivParams.Parameters is KeyParameter key)) throw new ArgumentException(algorithmName + " init parameters must include a key"); - } - KeyParameter key = (KeyParameter)ivParams.Parameters; byte[] key8 = key.GetKey(); if (key8.Length != SCHWAEMM_KEY_LEN >> 3) - { throw new ArgumentException(algorithmName + " key must be 128 bits long"); - } + Pack.LE_To_UInt32(key8, 0, k, 0, KEY_WORDS); initialised = true; reset(false); @@ -345,9 +333,9 @@ namespace Org.BouncyCastle.Crypto.Engines throw new ArgumentException(algorithmName + ": AAD cannot be added after reading a full block(" + GetBlockSize() + " bytes) of input for " + (forEncryption ? "encryption" : "decryption")); } - aadData.Write(new byte[] { input }, 0, 1); - } + aadData.WriteByte(input); + } public void ProcessAadBytes(byte[] input, int inOff, int len) { @@ -356,61 +344,53 @@ namespace Org.BouncyCastle.Crypto.Engines throw new ArgumentException(algorithmName + ": AAD cannot be added after reading a full block(" + GetBlockSize() + " bytes) of input for " + (forEncryption ? "encryption" : "decryption")); } - if (inOff + len > input.Length) - { - throw new DataLengthException(algorithmName + " input buffer too short"); - } + + Check.DataLength(input, inOff, len, "input buffer too short"); + aadData.Write(input, inOff, len); } - public int ProcessByte(byte input, byte[] output, int outOff) { - return ProcessBytes(new byte[] { input }, 0, 1, output, outOff); + return ProcessBytes(new byte[]{ input }, 0, 1, output, outOff); } - public int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { if (!initialised) - { throw new ArgumentException(algorithmName + " Need call init function before encryption/decryption"); - } - if (inOff + len > input.Length) - { - throw new DataLengthException(algorithmName + " input buffer too short"); - } + + Check.DataLength(input, inOff, len, "input buffer too short"); + message.Write(input, inOff, len); + + int msgLen = Convert.ToInt32(message.Length); + len = 0; - if ((forEncryption && (int)message.Length > GetBlockSize()) || - (!forEncryption && (int)message.Length - TAG_BYTES > GetBlockSize())) + if ((forEncryption && msgLen > GetBlockSize()) || + (!forEncryption && msgLen - TAG_BYTES > GetBlockSize())) { - len = ((int)message.Length - (forEncryption ? 0 : TAG_BYTES)); - if (len / RATE_BYTES * RATE_BYTES + outOff > output.Length) - { - throw new OutputLengthException(algorithmName + " output buffer is too short"); - } + len = msgLen - (forEncryption ? 0 : TAG_BYTES); + Check.OutputLength(output, outOff, len / RATE_BYTES * RATE_BYTES, "output buffer is too short"); byte[] m = message.GetBuffer(); ProcessAssocData(state); if (len != 0) { len = ProcessPlainText(state, output, m, 0, len); } - int mlen = (int)message.Length; message.SetLength(0); - message.Write(m, len, mlen - len); + // TODO Sketchy writing back to the buffer after setting length to 0 + message.Write(m, len, msgLen - len); } return len; } - public int DoFinal(byte[] output, int outOff) { if (!initialised) - { - throw new ArgumentException(algorithmName + " needs call init function before dofinal"); - } - int inlen = (int)message.Length - (forEncryption ? 0 : TAG_BYTES); + throw new ArgumentException(algorithmName + " needs to call Init before DoFinal"); + + int inlen = Convert.ToInt32(message.Length) - (forEncryption ? 0 : TAG_BYTES); if ((forEncryption && inlen + TAG_BYTES + outOff > output.Length) || (!forEncryption && inlen + outOff > output.Length)) { @@ -495,25 +475,23 @@ namespace Org.BouncyCastle.Crypto.Engines return tag; } - public int GetUpdateOutputSize(int len) { + // TODO return len; } - public int GetOutputSize(int len) { + // TODO return len + TAG_BYTES; } - public void Reset() { if (!initialised) - { - throw new ArgumentException(algorithmName + " needs call init function before reset"); - } + throw new ArgumentException(algorithmName + " needs to call Init before Reset"); + reset(true); } diff --git a/crypto/src/crypto/engines/XoodyakEngine.cs b/crypto/src/crypto/engines/XoodyakEngine.cs index e0263272e..2eb563525 100644 --- a/crypto/src/crypto/engines/XoodyakEngine.cs +++ b/crypto/src/crypto/engines/XoodyakEngine.cs @@ -13,11 +13,23 @@ namespace Org.BouncyCastle.Crypto.Engines *

* Xoodyak with reference to C Reference Impl from: https://github.com/XKCP/XKCP *

-*/ + */ public sealed class XoodyakEngine + // TODO IAeadCipher only : IAeadBlockCipher { - private bool forEncryption; + private enum MODE + { + ModeHash, + ModeKeyed + } + + private const int Rkin = 44; + + private static readonly uint[] RC = { 0x00000058U, 0x00000038U, 0x000003C0U, 0x000000D0U, 0x00000120U, + 0x00000014U, 0x00000060U, 0x0000002CU, 0x00000380U, 0x000000F0U, 0x000001A0U, 0x00000012U }; + + private bool forEncryption = true; // Safe output sizes before initialization private byte[] state; private int phase; private MODE mode; @@ -33,49 +45,36 @@ namespace Org.BouncyCastle.Crypto.Engines private const int NCOLUMS = 4; private const int MAXROUNDS = 12; private const int TAGLEN = 16; - const int Rkin = 44; private byte[] tag; - private readonly uint[] RC = {0x00000058, 0x00000038, 0x000003C0, 0x000000D0, 0x00000120, 0x00000014, 0x00000060, - 0x0000002C, 0x00000380, 0x000000F0, 0x000001A0, 0x00000012}; private bool aadFinished; private bool encrypted; private bool initialised = false; - public string AlgorithmName => "Xoodak AEAD"; - - public IBlockCipher UnderlyingCipher => throw new NotImplementedException(); private MemoryStream aadData = new MemoryStream(); private MemoryStream message = new MemoryStream(); - enum MODE - { - ModeHash, - ModeKeyed - } + public string AlgorithmName => "Xoodak AEAD"; + + public IBlockCipher UnderlyingCipher => throw new NotImplementedException(); - public void Init(bool forEncryption, ICipherParameters param) + public void Init(bool forEncryption, ICipherParameters parameters) { this.forEncryption = forEncryption; - if (!(param is ParametersWithIV)) - { + + if (!(parameters is ParametersWithIV ivParams)) throw new ArgumentException("Xoodyak init parameters must include an IV"); - } - ParametersWithIV ivParams = (ParametersWithIV)param; + iv = ivParams.GetIV(); if (iv == null || iv.Length != 16) - { throw new ArgumentException("Xoodyak requires exactly 16 bytes of IV"); - } - if (!(ivParams.Parameters is KeyParameter)) - { + + if (!(ivParams.Parameters is KeyParameter key)) throw new ArgumentException("Xoodyak init parameters must include a key"); - } - KeyParameter key = (KeyParameter)ivParams.Parameters; + K = key.GetKey(); if (K.Length != 16) - { throw new ArgumentException("Xoodyak key must be 128 bits long"); - } + state = new byte[48]; tag = new byte[TAGLEN]; initialised = true; @@ -89,9 +88,9 @@ namespace Org.BouncyCastle.Crypto.Engines throw new ArgumentException("AAD cannot be added after reading a full block(" + GetBlockSize() + " bytes) of input for " + (forEncryption ? "encryption" : "decryption")); } - aadData.Write(new byte[] { input }, 0, 1); - } + aadData.WriteByte(input); + } public void ProcessAadBytes(byte[] input, int inOff, int len) { @@ -100,17 +99,15 @@ namespace Org.BouncyCastle.Crypto.Engines throw new ArgumentException("AAD cannot be added after reading a full block(" + GetBlockSize() + " bytes) of input for " + (forEncryption ? "encryption" : "decryption")); } - if ((inOff + len) > input.Length) - { - throw new DataLengthException("input buffer too short"); - } + + Check.DataLength(input, inOff, len, "input buffer too short"); + aadData.Write(input, inOff, len); } - - public int ProcessByte(byte input, byte[] output, int outOff) + public int ProcessByte(byte input, byte[] outBytes, int outOff) { - return ProcessBytes(new byte[] { input }, 0, 1, output, outOff); + return ProcessBytes(new byte[]{ input }, 0, 1, outBytes, outOff); } private void processAAD() @@ -123,7 +120,7 @@ namespace Org.BouncyCastle.Crypto.Engines } } - public int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + public int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff) { if (!initialised) throw new ArgumentException("Need to call Init before encryption/decryption"); @@ -131,17 +128,17 @@ namespace Org.BouncyCastle.Crypto.Engines if (mode != MODE.ModeKeyed) throw new ArgumentException("Xoodyak has not been initialised"); - Check.DataLength(input, inOff, len, "input buffer too short"); + Check.DataLength(inBytes, inOff, len, "input buffer too short"); - message.Write(input, inOff, len); + message.Write(inBytes, inOff, len); int blockLen = (int)message.Length - (forEncryption ? 0 : TAGLEN); if (blockLen >= GetBlockSize()) { byte[] blocks = message.GetBuffer(); len = blockLen / GetBlockSize() * GetBlockSize(); - Check.OutputLength(output, outOff, len, "output buffer is too short"); + Check.OutputLength(outBytes, outOff, len, "output buffer is too short"); processAAD(); - encrypt(blocks, 0, len, output, outOff); + encrypt(blocks, 0, len, outBytes, outOff); int messageLen = (int)message.Length; message.SetLength(0); message.Write(blocks, len, messageLen - len); @@ -186,15 +183,15 @@ namespace Org.BouncyCastle.Crypto.Engines } - public int DoFinal(byte[] output, int outOff) + public int DoFinal(byte[] outBytes, int outOff) { if (!initialised) throw new ArgumentException("Need to call Init before encryption/decryption"); byte[] blocks = message.GetBuffer(); int len = (int)message.Length; - if ((forEncryption && len + TAGLEN + outOff > output.Length) || - (!forEncryption && len - TAGLEN + outOff > output.Length)) + if ((forEncryption && len + TAGLEN + outOff > outBytes.Length) || + (!forEncryption && len - TAGLEN + outOff > outBytes.Length)) { throw new OutputLengthException("output buffer too short"); } @@ -202,18 +199,18 @@ namespace Org.BouncyCastle.Crypto.Engines int rv = 0; if (forEncryption) { - encrypt(blocks, 0, len, output, outOff); + encrypt(blocks, 0, len, outBytes, outOff); outOff += len; tag = new byte[TAGLEN]; Up(tag, TAGLEN, 0x40); - Array.Copy(tag, 0, output, outOff, TAGLEN); + Array.Copy(tag, 0, outBytes, outOff, TAGLEN); rv = len + TAGLEN; } else { int inOff = len - TAGLEN; rv = inOff; - encrypt(blocks, 0, inOff, output, outOff); + encrypt(blocks, 0, inOff, outBytes, outOff); tag = new byte[TAGLEN]; Up(tag, TAGLEN, 0x40); @@ -231,11 +228,13 @@ namespace Org.BouncyCastle.Crypto.Engines public int GetUpdateOutputSize(int len) { + // TODO return len; } public int GetOutputSize(int len) { + // TODO return len + TAGLEN; } @@ -427,8 +426,6 @@ namespace Org.BouncyCastle.Crypto.Engines rv.AsSpan(0, len).CopyTo(output); return rv.Length; } - #endif - } -} \ No newline at end of file +} diff --git a/crypto/test/src/crypto/test/ElephantTest.cs b/crypto/test/src/crypto/test/ElephantTest.cs index 406be7c6f..7e7e131ab 100644 --- a/crypto/test/src/crypto/test/ElephantTest.cs +++ b/crypto/test/src/crypto/test/ElephantTest.cs @@ -270,7 +270,7 @@ namespace Org.BouncyCastle.Crypto.Tests elephantEngine.DoFinal(mac2, 0); if (!Arrays.AreEqual(mac1, mac2)) { - Assert.Fail("mac should match for the same AAD with different ways of inputing"); + Assert.Fail("mac should match for the same AAD with different ways of inputting"); } byte[] c2 = new byte[elephantEngine.GetOutputSize(10)]; diff --git a/crypto/test/src/crypto/test/ISAPTest.cs b/crypto/test/src/crypto/test/ISAPTest.cs index e7b0bd25c..500026bf9 100644 --- a/crypto/test/src/crypto/test/ISAPTest.cs +++ b/crypto/test/src/crypto/test/ISAPTest.cs @@ -330,7 +330,7 @@ namespace Org.BouncyCastle.Crypto.Tests isapEngine.DoFinal(mac2, 0); if (!Arrays.AreEqual(mac1, mac2)) { - Assert.Fail("mac should match for the same AAD with different ways of inputing"); + Assert.Fail("mac should match for the same AAD with different ways of inputting"); } byte[] c2 = new byte[isapEngine.GetOutputSize(10)]; diff --git a/crypto/test/src/crypto/test/PhotonBeetleTest.cs b/crypto/test/src/crypto/test/PhotonBeetleTest.cs index 80e34ad4b..cd551432c 100644 --- a/crypto/test/src/crypto/test/PhotonBeetleTest.cs +++ b/crypto/test/src/crypto/test/PhotonBeetleTest.cs @@ -310,7 +310,7 @@ namespace Org.BouncyCastle.Crypto.Tests aeadBlockCipher.DoFinal(mac2, 0); if (!Arrays.AreEqual(mac1, mac2)) { - Assert.Fail("mac should match for the same AAD with different ways of inputing"); + Assert.Fail("mac should match for the same AAD with different ways of inputting"); } byte[] c2 = new byte[aeadBlockCipher.GetOutputSize(10)]; diff --git a/crypto/test/src/crypto/test/SparkleTest.cs b/crypto/test/src/crypto/test/SparkleTest.cs index 245a1831c..ea4520226 100644 --- a/crypto/test/src/crypto/test/SparkleTest.cs +++ b/crypto/test/src/crypto/test/SparkleTest.cs @@ -324,7 +324,7 @@ namespace Org.BouncyCastle.Crypto.Tests sparkleEngine.DoFinal(mac2, 0); if (!Arrays.AreEqual(mac1, mac2)) { - Assert.Fail("mac should match for the same AAD with different ways of inputing"); + Assert.Fail("mac should match for the same AAD with different ways of inputting"); } byte[] c2 = new byte[sparkleEngine.GetOutputSize(10)]; diff --git a/crypto/test/src/crypto/test/XoodyakTest.cs b/crypto/test/src/crypto/test/XoodyakTest.cs index d8664e82f..b73d998f4 100644 --- a/crypto/test/src/crypto/test/XoodyakTest.cs +++ b/crypto/test/src/crypto/test/XoodyakTest.cs @@ -300,7 +300,7 @@ namespace Org.BouncyCastle.Crypto.Tests xoodyakEngine.DoFinal(mac2, 0); if (!Arrays.AreEqual(mac1, mac2)) { - Assert.Fail("mac should match for the same AAD with different ways of inputing"); + Assert.Fail("mac should match for the same AAD with different ways of inputting"); } byte[] c2 = new byte[xoodyakEngine.GetOutputSize(10)]; -- cgit 1.4.1