436 lines
10 KiB
C#
436 lines
10 KiB
C#
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);
|
|
}
|
|
}
|