using System; using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests; public class KeccakDigest : IDigest, IMemoable { private static readonly ulong[] KeccakRoundConstants = new ulong[24] { 1uL, 32898uL, 9223372036854808714uL, 9223372039002292224uL, 32907uL, 2147483649uL, 9223372039002292353uL, 9223372036854808585uL, 138uL, 136uL, 2147516425uL, 2147483658uL, 2147516555uL, 9223372036854775947uL, 9223372036854808713uL, 9223372036854808579uL, 9223372036854808578uL, 9223372036854775936uL, 32778uL, 9223372039002259466uL, 9223372039002292353uL, 9223372036854808704uL, 2147483649uL, 9223372039002292232uL }; private ulong[] state = new ulong[25]; protected byte[] dataQueue = new byte[192]; protected int rate; protected int bitsInQueue; protected int fixedOutputLength; protected bool squeezing; public virtual string AlgorithmName => "Keccak-" + fixedOutputLength; public KeccakDigest() : this(288) { } public KeccakDigest(int bitLength) { Init(bitLength); } public KeccakDigest(KeccakDigest source) { CopyIn(source); } private void CopyIn(KeccakDigest source) { Array.Copy(source.state, 0, state, 0, source.state.Length); Array.Copy(source.dataQueue, 0, dataQueue, 0, source.dataQueue.Length); rate = source.rate; bitsInQueue = source.bitsInQueue; fixedOutputLength = source.fixedOutputLength; squeezing = source.squeezing; } public virtual int GetDigestSize() { return fixedOutputLength >> 3; } public virtual void Update(byte input) { Absorb(new byte[1] { input }, 0, 1); } public virtual void BlockUpdate(byte[] input, int inOff, int len) { Absorb(input, inOff, len); } public virtual int DoFinal(byte[] output, int outOff) { Squeeze(output, outOff, fixedOutputLength); Reset(); return GetDigestSize(); } protected virtual int DoFinal(byte[] output, int outOff, byte partialByte, int partialBits) { if (partialBits > 0) { AbsorbBits(partialByte, partialBits); } Squeeze(output, outOff, fixedOutputLength); Reset(); return GetDigestSize(); } public virtual void Reset() { Init(fixedOutputLength); } public virtual int GetByteLength() { return rate >> 3; } private void Init(int bitLength) { switch (bitLength) { case 128: case 224: case 256: case 288: case 384: case 512: InitSponge(1600 - (bitLength << 1)); break; default: throw new ArgumentException("must be one of 128, 224, 256, 288, 384, or 512.", "bitLength"); } } private void InitSponge(int rate) { if (rate <= 0 || rate >= 1600 || (rate & 0x3F) != 0) { throw new InvalidOperationException("invalid rate value"); } this.rate = rate; Array.Clear(state, 0, state.Length); Arrays.Fill(dataQueue, 0); bitsInQueue = 0; squeezing = false; fixedOutputLength = 1600 - rate >> 1; } protected void Absorb(byte[] data, int off, int len) { if ((bitsInQueue & 7) != 0) { throw new InvalidOperationException("attempt to absorb with odd length queue"); } if (squeezing) { throw new InvalidOperationException("attempt to absorb while squeezing"); } int num = bitsInQueue >> 3; int num2 = rate >> 3; int num3 = 0; while (num3 < len) { if (num == 0 && num3 <= len - num2) { do { KeccakAbsorb(data, off + num3); num3 += num2; } while (num3 <= len - num2); continue; } int num4 = System.Math.Min(num2 - num, len - num3); Array.Copy(data, off + num3, dataQueue, num, num4); num += num4; num3 += num4; if (num == num2) { KeccakAbsorb(dataQueue, 0); num = 0; } } bitsInQueue = num << 3; } protected void AbsorbBits(int data, int bits) { if (bits < 1 || bits > 7) { throw new ArgumentException("must be in the range 1 to 7", "bits"); } if ((bitsInQueue & 7) != 0) { throw new InvalidOperationException("attempt to absorb with odd length queue"); } if (squeezing) { throw new InvalidOperationException("attempt to absorb while squeezing"); } int num = (1 << bits) - 1; dataQueue[bitsInQueue >> 3] = (byte)(data & num); bitsInQueue += bits; } private void PadAndSwitchToSqueezingPhase() { byte[] array2; byte[] array = (array2 = dataQueue); int num = bitsInQueue >> 3; nint num2 = num; array[num] = (byte)(array2[num2] | (byte)(1 << (bitsInQueue & 7))); if (++bitsInQueue == rate) { KeccakAbsorb(dataQueue, 0); bitsInQueue = 0; } int num3 = bitsInQueue >> 6; int num4 = bitsInQueue & 0x3F; int num5 = 0; ulong[] array4; for (int i = 0; i < num3; i++) { ulong[] array3 = (array4 = state); int num6 = i; num2 = num6; array3[num6] = array4[num2] ^ Pack.LE_To_UInt64(dataQueue, num5); num5 += 8; } if (num4 > 0) { ulong num7 = (ulong)((1L << num4) - 1); ulong[] array5 = (array4 = state); num2 = num3; array5[num3] = array4[num2] ^ (Pack.LE_To_UInt64(dataQueue, num5) & num7); } ulong[] array6 = (array4 = state); int num8 = rate - 1 >> 6; num2 = num8; array6[num8] = array4[num2] ^ 0x8000000000000000uL; KeccakPermutation(); KeccakExtract(); bitsInQueue = rate; squeezing = true; } protected void Squeeze(byte[] output, int offset, long outputLength) { if (!squeezing) { PadAndSwitchToSqueezingPhase(); } if ((outputLength & 7) != 0) { throw new InvalidOperationException("outputLength not a multiple of 8"); } int num2; for (long num = 0L; num < outputLength; num += num2) { if (bitsInQueue == 0) { KeccakPermutation(); KeccakExtract(); bitsInQueue = rate; } num2 = (int)System.Math.Min(bitsInQueue, outputLength - num); Array.Copy(dataQueue, rate - bitsInQueue >> 3, output, offset + (int)(num >> 3), num2 >> 3); bitsInQueue -= num2; } } private void KeccakAbsorb(byte[] data, int off) { int num = rate >> 6; for (int i = 0; i < num; i++) { ulong[] array2; ulong[] array = (array2 = state); int num2 = i; nint num3 = num2; array[num2] = array2[num3] ^ Pack.LE_To_UInt64(data, off); off += 8; } KeccakPermutation(); } private void KeccakExtract() { Pack.UInt64_To_LE(state, 0, rate >> 6, dataQueue, 0); } private void KeccakPermutation() { ulong[] array = state; ulong num = array[0]; ulong num2 = array[1]; ulong num3 = array[2]; ulong num4 = array[3]; ulong num5 = array[4]; ulong num6 = array[5]; ulong num7 = array[6]; ulong num8 = array[7]; ulong num9 = array[8]; ulong num10 = array[9]; ulong num11 = array[10]; ulong num12 = array[11]; ulong num13 = array[12]; ulong num14 = array[13]; ulong num15 = array[14]; ulong num16 = array[15]; ulong num17 = array[16]; ulong num18 = array[17]; ulong num19 = array[18]; ulong num20 = array[19]; ulong num21 = array[20]; ulong num22 = array[21]; ulong num23 = array[22]; ulong num24 = array[23]; ulong num25 = array[24]; for (int i = 0; i < 24; i++) { ulong num26 = num ^ num6 ^ num11 ^ num16 ^ num21; ulong num27 = num2 ^ num7 ^ num12 ^ num17 ^ num22; ulong num28 = num3 ^ num8 ^ num13 ^ num18 ^ num23; ulong num29 = num4 ^ num9 ^ num14 ^ num19 ^ num24; ulong num30 = num5 ^ num10 ^ num15 ^ num20 ^ num25; ulong num31 = ((num27 << 1) | (num27 >> 63)) ^ num30; ulong num32 = ((num28 << 1) | (num28 >> 63)) ^ num26; ulong num33 = ((num29 << 1) | (num29 >> 63)) ^ num27; ulong num34 = ((num30 << 1) | (num30 >> 63)) ^ num28; ulong num35 = ((num26 << 1) | (num26 >> 63)) ^ num29; num ^= num31; num6 ^= num31; num11 ^= num31; num16 ^= num31; num21 ^= num31; num2 ^= num32; num7 ^= num32; num12 ^= num32; num17 ^= num32; num22 ^= num32; num3 ^= num33; num8 ^= num33; num13 ^= num33; num18 ^= num33; num23 ^= num33; num4 ^= num34; num9 ^= num34; num14 ^= num34; num19 ^= num34; num24 ^= num34; num5 ^= num35; num10 ^= num35; num15 ^= num35; num20 ^= num35; num25 ^= num35; num27 = (num2 << 1) | (num2 >> 63); num2 = (num7 << 44) | (num7 >> 20); num7 = (num10 << 20) | (num10 >> 44); num10 = (num23 << 61) | (num23 >> 3); num23 = (num15 << 39) | (num15 >> 25); num15 = (num21 << 18) | (num21 >> 46); num21 = (num3 << 62) | (num3 >> 2); num3 = (num13 << 43) | (num13 >> 21); num13 = (num14 << 25) | (num14 >> 39); num14 = (num20 << 8) | (num20 >> 56); num20 = (num24 << 56) | (num24 >> 8); num24 = (num16 << 41) | (num16 >> 23); num16 = (num5 << 27) | (num5 >> 37); num5 = (num25 << 14) | (num25 >> 50); num25 = (num22 << 2) | (num22 >> 62); num22 = (num9 << 55) | (num9 >> 9); num9 = (num17 << 45) | (num17 >> 19); num17 = (num6 << 36) | (num6 >> 28); num6 = (num4 << 28) | (num4 >> 36); num4 = (num19 << 21) | (num19 >> 43); num19 = (num18 << 15) | (num18 >> 49); num18 = (num12 << 10) | (num12 >> 54); num12 = (num8 << 6) | (num8 >> 58); num8 = (num11 << 3) | (num11 >> 61); num11 = num27; num26 = num ^ (~num2 & num3); num27 = num2 ^ (~num3 & num4); num3 ^= ~num4 & num5; num4 ^= ~num5 & num; num5 ^= ~num & num2; num = num26; num2 = num27; num26 = num6 ^ (~num7 & num8); num27 = num7 ^ (~num8 & num9); num8 ^= ~num9 & num10; num9 ^= ~num10 & num6; num10 ^= ~num6 & num7; num6 = num26; num7 = num27; num26 = num11 ^ (~num12 & num13); num27 = num12 ^ (~num13 & num14); num13 ^= ~num14 & num15; num14 ^= ~num15 & num11; num15 ^= ~num11 & num12; num11 = num26; num12 = num27; num26 = num16 ^ (~num17 & num18); num27 = num17 ^ (~num18 & num19); num18 ^= ~num19 & num20; num19 ^= ~num20 & num16; num20 ^= ~num16 & num17; num16 = num26; num17 = num27; num26 = num21 ^ (~num22 & num23); num27 = num22 ^ (~num23 & num24); num23 ^= ~num24 & num25; num24 ^= ~num25 & num21; num25 ^= ~num21 & num22; num21 = num26; num22 = num27; num ^= KeccakRoundConstants[i]; } array[0] = num; array[1] = num2; array[2] = num3; array[3] = num4; array[4] = num5; array[5] = num6; array[6] = num7; array[7] = num8; array[8] = num9; array[9] = num10; array[10] = num11; array[11] = num12; array[12] = num13; array[13] = num14; array[14] = num15; array[15] = num16; array[16] = num17; array[17] = num18; array[18] = num19; array[19] = num20; array[20] = num21; array[21] = num22; array[22] = num23; array[23] = num24; array[24] = num25; } public virtual IMemoable Copy() { return new KeccakDigest(this); } public virtual void Reset(IMemoable other) { CopyIn((KeccakDigest)other); } }