KeccakDigest
Implementation of Keccak based on following KeccakNISTInterface.c from http://keccak.noekeon.org/
using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
using System;
namespace Org.BouncyCastle.Crypto.Digests
{
public class KeccakDigest : IDigest, IMemoable
{
private static readonly ulong[] KeccakRoundConstants = new ulong[24] {
1,
32898,
9223372036854808714,
9223372039002292224,
32907,
2147483649,
9223372039002292353,
9223372036854808585,
138,
136,
2147516425,
2147483658,
2147516555,
9223372036854775947,
9223372036854808713,
9223372036854808579,
9223372036854808578,
9223372036854775936,
32778,
9223372039002259466,
9223372039002292353,
9223372036854808704,
2147483649,
9223372039002292232
};
private readonly ulong[] state = new ulong[25];
protected byte[] dataQueue = new byte[192];
protected int rate;
protected int bitsInQueue;
protected internal int fixedOutputLength;
protected bool squeezing;
public virtual string AlgorithmName => "Keccak-" + fixedOutputLength.ToString();
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(input);
}
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 & 63) != 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)
{
if ((bitsInQueue & 7) != 0)
throw new InvalidOperationException("attempt to absorb with odd length queue");
if (squeezing)
throw new InvalidOperationException("attempt to absorb while squeezing");
dataQueue[bitsInQueue >> 3] = data;
if ((bitsInQueue += 8) == rate) {
KeccakAbsorb(dataQueue, 0);
bitsInQueue = 0;
}
}
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 = num2 - num;
if (len < num3) {
Array.Copy(data, off, dataQueue, num, len);
bitsInQueue += len << 3;
} else {
int num4 = 0;
if (num > 0) {
Array.Copy(data, off, dataQueue, num, num3);
num4 += num3;
KeccakAbsorb(dataQueue, 0);
}
int num5;
while ((num5 = len - num4) >= num2) {
KeccakAbsorb(data, off + num4);
num4 += num2;
}
Array.Copy(data, off + num4, dataQueue, 0, num5);
bitsInQueue = num5 << 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()
{
dataQueue[bitsInQueue >> 3] |= (byte)(1 << (bitsInQueue & 7));
if (++bitsInQueue == rate)
KeccakAbsorb(dataQueue, 0);
else {
int num = bitsInQueue >> 6;
int num2 = bitsInQueue & 63;
int num3 = 0;
for (int i = 0; i < num; i++) {
state[i] ^= Pack.LE_To_UInt64(dataQueue, num3);
num3 += 8;
}
if (num2 > 0) {
ulong num4 = (ulong)((1 << num2) - 1);
state[num] ^= (Pack.LE_To_UInt64(dataQueue, num3) & num4);
}
}
state[rate - 1 >> 6] ^= 9223372036854775808;
bitsInQueue = 0;
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 = 0; num < outputLength; num += num2) {
if (bitsInQueue == 0)
KeccakExtract();
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++) {
state[i] ^= Pack.LE_To_UInt64(data, off);
off += 8;
}
KeccakPermutation(state);
}
private void KeccakExtract()
{
KeccakPermutation(state);
Pack.UInt64_To_LE(state, 0, rate >> 6, dataQueue, 0);
bitsInQueue = rate;
}
internal static void KeccakPermutation(ulong[] A)
{
ulong num36 = A[24];
ulong num = A[0];
ulong num2 = A[1];
ulong num3 = A[2];
ulong num4 = A[3];
ulong num5 = A[4];
ulong num6 = A[5];
ulong num7 = A[6];
ulong num8 = A[7];
ulong num9 = A[8];
ulong num10 = A[9];
ulong num11 = A[10];
ulong num12 = A[11];
ulong num13 = A[12];
ulong num14 = A[13];
ulong num15 = A[14];
ulong num16 = A[15];
ulong num17 = A[16];
ulong num18 = A[17];
ulong num19 = A[18];
ulong num20 = A[19];
ulong num21 = A[20];
ulong num22 = A[21];
ulong num23 = A[22];
ulong num24 = A[23];
ulong num25 = A[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 = Longs.RotateLeft(num27, 1) ^ num30;
ulong num32 = Longs.RotateLeft(num28, 1) ^ num26;
ulong num33 = Longs.RotateLeft(num29, 1) ^ num27;
ulong num34 = Longs.RotateLeft(num30, 1) ^ num28;
ulong num35 = Longs.RotateLeft(num26, 1) ^ 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 = Longs.RotateLeft(num2, 1);
num2 = Longs.RotateLeft(num7, 44);
num7 = Longs.RotateLeft(num10, 20);
num10 = Longs.RotateLeft(num23, 61);
num23 = Longs.RotateLeft(num15, 39);
num15 = Longs.RotateLeft(num21, 18);
num21 = Longs.RotateLeft(num3, 62);
num3 = Longs.RotateLeft(num13, 43);
num13 = Longs.RotateLeft(num14, 25);
num14 = Longs.RotateLeft(num20, 8);
num20 = Longs.RotateLeft(num24, 56);
num24 = Longs.RotateLeft(num16, 41);
num16 = Longs.RotateLeft(num5, 27);
num5 = Longs.RotateLeft(num25, 14);
num25 = Longs.RotateLeft(num22, 2);
num22 = Longs.RotateLeft(num9, 55);
num9 = Longs.RotateLeft(num17, 45);
num17 = Longs.RotateLeft(num6, 36);
num6 = Longs.RotateLeft(num4, 28);
num4 = Longs.RotateLeft(num19, 21);
num19 = Longs.RotateLeft(num18, 15);
num18 = Longs.RotateLeft(num12, 10);
num12 = Longs.RotateLeft(num8, 6);
num8 = Longs.RotateLeft(num11, 3);
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];
}
A[0] = num;
A[1] = num2;
A[2] = num3;
A[3] = num4;
A[4] = num5;
A[5] = num6;
A[6] = num7;
A[7] = num8;
A[8] = num9;
A[9] = num10;
A[10] = num11;
A[11] = num12;
A[12] = num13;
A[13] = num14;
A[14] = num15;
A[15] = num16;
A[16] = num17;
A[17] = num18;
A[18] = num19;
A[19] = num20;
A[20] = num21;
A[21] = num22;
A[22] = num23;
A[23] = num24;
A[24] = num25;
}
public virtual IMemoable Copy()
{
return new KeccakDigest(this);
}
public virtual void Reset(IMemoable other)
{
CopyIn((KeccakDigest)other);
}
}
}