<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />

AsconXof

public sealed class AsconXof : IXof, IDigest
ASCON v1.2 XOF, https://ascon.iaik.tugraz.at/ .
using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; using System; using System.Runtime.CompilerServices; namespace Org.BouncyCastle.Crypto.Digests { public sealed class AsconXof : IXof, IDigest { public enum AsconParameters { AsconXof, AsconXofA } private readonly AsconParameters m_asconParameters; private readonly int ASCON_PB_ROUNDS; private ulong x0; private ulong x1; private ulong x2; private ulong x3; private ulong x4; private readonly byte[] m_buf = new byte[8]; private int m_bufPos; private bool m_squeezing; public string AlgorithmName { get { switch (m_asconParameters) { case AsconParameters.AsconXof: return "Ascon-Xof"; case AsconParameters.AsconXofA: return "Ascon-XofA"; default: throw new InvalidOperationException(); } } } public AsconXof(AsconParameters parameters) { m_asconParameters = parameters; switch (parameters) { case AsconParameters.AsconXof: ASCON_PB_ROUNDS = 12; break; case AsconParameters.AsconXofA: ASCON_PB_ROUNDS = 8; break; default: throw new ArgumentException("Invalid parameter settings for Ascon XOF"); } Reset(); } public int GetDigestSize() { return 32; } public int GetByteLength() { return 8; } public void Update(byte input) { if (m_squeezing) throw new InvalidOperationException("attempt to absorb while squeezing"); m_buf[m_bufPos] = input; if (++m_bufPos == 8) { x0 ^= Pack.BE_To_UInt64(m_buf, 0); P(ASCON_PB_ROUNDS); m_bufPos = 0; } } public void BlockUpdate(byte[] input, int inOff, int inLen) { Check.DataLength(input, inOff, inLen, "input buffer too short"); BlockUpdate(input.AsSpan(inOff, inLen)); } public void BlockUpdate(ReadOnlySpan<byte> input) { if (m_squeezing) throw new InvalidOperationException("attempt to absorb while squeezing"); int num = 8 - m_bufPos; if (input.Length < num) { input.CopyTo(m_buf.AsSpan(m_bufPos)); m_bufPos += input.Length; } else { if (m_bufPos > 0) { input.Slice(0, num).CopyTo(m_buf.AsSpan(m_bufPos)); x0 ^= Pack.BE_To_UInt64(m_buf); P(ASCON_PB_ROUNDS); int num2 = num; input = input.Slice(num2, input.Length - num2); } while (input.Length >= 8) { x0 ^= Pack.BE_To_UInt64(input); P(ASCON_PB_ROUNDS); input = input.Slice(8, input.Length - 8); } input.CopyTo(m_buf); m_bufPos = input.Length; } } public int DoFinal(byte[] output, int outOff) { return OutputFinal(output, outOff, GetDigestSize()); } public int DoFinal(Span<byte> output) { int digestSize = GetDigestSize(); Check.OutputLength(output, digestSize, "output buffer is too short"); return OutputFinal(output.Slice(0, digestSize)); } public int OutputFinal(byte[] output, int outOff, int outLen) { Check.OutputLength(output, outOff, outLen, "output buffer is too short"); return OutputFinal(output.AsSpan(outOff, outLen)); } public int OutputFinal(Span<byte> output) { int result = Output(output); Reset(); return result; } public int Output(byte[] output, int outOff, int outLen) { Check.OutputLength(output, outOff, outLen, "output buffer is too short"); return Output(output.AsSpan(outOff, outLen)); } public int Output(Span<byte> output) { int length = output.Length; if (!m_squeezing) { FinishAbsorbing(); if (output.Length >= 8) { Pack.UInt64_To_BE(x0, output); output = output.Slice(8, output.Length - 8); } else { Pack.UInt64_To_BE(x0, m_buf); m_bufPos = 0; } } if (m_bufPos < 8) { int num = 8 - m_bufPos; if (output.Length <= num) { output.CopyFrom(m_buf.AsSpan(m_bufPos)); m_bufPos += output.Length; return length; } output.Slice(0, num).CopyFrom(m_buf.AsSpan(m_bufPos)); int num2 = num; output = output.Slice(num2, output.Length - num2); } while (output.Length >= 8) { P(ASCON_PB_ROUNDS); Pack.UInt64_To_BE(x0, output); output = output.Slice(8, output.Length - 8); } if (!output.IsEmpty) { P(ASCON_PB_ROUNDS); Pack.UInt64_To_BE(x0, m_buf); output.CopyFrom(m_buf); } m_bufPos = output.Length; return length; } public void Reset() { Array.Clear(m_buf, 0, m_buf.Length); m_bufPos = 0; m_squeezing = false; switch (m_asconParameters) { case AsconParameters.AsconXof: x0 = 13077933504456348694; x1 = 3121280575360345120; x2 = 7395939140700676632; x3 = 6533890155656471820; x4 = 5710016986865767350; break; case AsconParameters.AsconXofA: x0 = 4940560291654768690; x1 = 14811614245468591410; x2 = 17849209150987444521; x3 = 2623493988082852443; x4 = 12162917349548726079; break; default: throw new InvalidOperationException(); } } private void FinishAbsorbing() { m_buf[m_bufPos] = 128; x0 ^= (ulong)((long)Pack.BE_To_UInt64(m_buf, 0) & (-1 << 56 - (m_bufPos << 3))); P(12); m_bufPos = 8; m_squeezing = true; } private void P(int nr) { if (nr == 12) { ROUND(240); ROUND(225); ROUND(210); ROUND(195); } ROUND(180); ROUND(165); ROUND(150); ROUND(135); ROUND(120); ROUND(105); ROUND(90); ROUND(75); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ROUND(ulong c) { ulong num = x0 ^ x1 ^ x2 ^ x3 ^ c ^ (x1 & (x0 ^ x2 ^ x4 ^ c)); ulong num2 = x0 ^ x2 ^ x3 ^ x4 ^ c ^ ((x1 ^ x2 ^ c) & (x1 ^ x3)); ulong num3 = x1 ^ x2 ^ x4 ^ c ^ (x3 & x4); ulong num4 = x0 ^ x1 ^ x2 ^ c ^ (~x0 & (x3 ^ x4)); ulong num5 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1); x0 = (num ^ Longs.RotateRight(num, 19) ^ Longs.RotateRight(num, 28)); x1 = (num2 ^ Longs.RotateRight(num2, 39) ^ Longs.RotateRight(num2, 61)); x2 = ~(num3 ^ Longs.RotateRight(num3, 1) ^ Longs.RotateRight(num3, 6)); x3 = (num4 ^ Longs.RotateRight(num4, 10) ^ Longs.RotateRight(num4, 17)); x4 = (num5 ^ Longs.RotateRight(num5, 7) ^ Longs.RotateRight(num5, 41)); } } }