Keccak Sponge (Sha3) Checked
using System; using System.Runtime.CompilerServices; [Serializable] public class SHA3Managed : HashAlgorithmEx { private readonly KeccakSpongeManaged ksm; public SHA3Managed(int size, ulong[] seed = null) : this(size, 24, seed) { } public SHA3Managed(int size) : this(size, 24) { } public SHA3Managed() : this(512, 24) { } public SHA3Managed(int size, int rounds = 24, ulong[] seed = null) { if (rounds > 24) throw new Exception($"Maximum rounds allowed is {24}"); var MaxBR = (64 >> 3) * 25; var sizeBytes = size >> 3; var rateBytes = MaxBR - (sizeBytes << 1); var MaxSize = ((MaxBR - 8) >> 1) << 3; if (rateBytes < 8) throw new Exception($"Maximum size allowed is {MaxSize} Bits with {64} bit Width specified. Specified Size is {size} Bits."); var outputLength = size >> 3; ksm = new KeccakSpongeManaged(rateBytes, 200 - rateBytes, KeccakSpongeManaged.KeccakDelimiter, outputLength, seed); ksm.Rounds = rounds; HashSizeValue = size; } public int HashLength => ksm.HashLength; public override void Initialize() { ksm.Initialize(); } protected override void HashCore(byte[] array, int ibStart, int cbSize) { Initialize(); ksm.Absorb(array, ibStart, cbSize); } protected override byte[] HashFinal() { return ksm.Squeeze(); } } [Serializable] public class KeccakSpongeManaged { public const int KeccakDelimiter = 6; public const int ShakeDelimiter = 31; private readonly int _delimiter; private readonly int _outputLength; private readonly int _rateBytes; private readonly ulong[] _roundConstants = { 0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000, 0x000000000000808B, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, 0x000000000000008A, 0x0000000000000088, 0x0000000080008009, 0x000000008000000A, 0x000000008000808B, 0x800000000000008B, 0x8000000000008089, 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A, 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 }; private readonly ulong[] _seed; private int _blockSize; private int _input; private int _output; private byte[] _result; private ulong[] _state; public int HashLength; public int Rounds = 24; public KeccakSpongeManaged(int rateBytes, int capacityBytes, int delimiter, int outputLength, ulong[] seed) { if (rateBytes + capacityBytes != 200 || (uint) (rateBytes % 8) > 0U) throw new ArgumentException($"rateBytes {rateBytes} + capacityBytes {capacityBytes}={rateBytes + capacityBytes} must be 200 or {rateBytes} must be a multiple of 8"); _rateBytes = rateBytes; _delimiter = delimiter; _outputLength = outputLength; HashLength = outputLength; _seed = seed; } public void Initialize() { _blockSize = 0; _input = 0; _output = 0; _state = new ulong[25]; _result = new byte[_outputLength]; if (_seed != null && _seed[0] != 0) { for (var i = 0; i < _seed.Length && i < _state.Length; ++i) _state[i] = _seed[i]; Permute(_state); } } public void Absorb(byte[] array, int start, int size) { while (size > 0) { _blockSize = Math.Min(size, _rateBytes); for (var index = start; index < _blockSize; ++index) { var num = Convert.ToByte(Buffer.GetByte(_state, index) ^ array[index + _input]); Buffer.SetByte(_state, index, num); } _input += _blockSize; size -= _blockSize; if (_blockSize == _rateBytes) { Permute(_state); _blockSize = 0; } } } public byte[] Squeeze() { Buffer.SetByte(_state, _blockSize, Convert.ToByte(Buffer.GetByte(_state, _blockSize) ^ _delimiter)); if ((_delimiter & 128) != 0 && _blockSize == _rateBytes - 1) Permute(_state); Buffer.SetByte(_state, _rateBytes - 1, Convert.ToByte(Buffer.GetByte(_state, _rateBytes - 1) ^ 128)); Permute(_state); var outputLength = _outputLength; while (outputLength > 0) { _blockSize = Math.Min(outputLength, _rateBytes); Buffer.BlockCopy(_state, 0, _result, _output, _blockSize); _output += _blockSize; outputLength -= _blockSize; if (outputLength > 0) Permute(_state); } return _result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Permute(ulong[] state) { for (var round = 0; round < Rounds; ++round) { Theta(out var C0, state, out var C1, out var C2, out var C3, out var C4); RhoPi(state); Chi(ref C0, state, ref C1, ref C2, ref C3, ref C4); Iota(round, state); } } private static void Theta(out ulong C0, ulong[] state, out ulong C1, out ulong C2, out ulong C3, out ulong C4) { C0 = state[0] ^ state[5] ^ state[10] ^ state[15] ^ state[20]; C1 = state[1] ^ state[6] ^ state[11] ^ state[16] ^ state[21]; C2 = state[2] ^ state[7] ^ state[12] ^ state[17] ^ state[22]; C3 = state[3] ^ state[8] ^ state[13] ^ state[18] ^ state[23]; C4 = state[4] ^ state[9] ^ state[14] ^ state[19] ^ state[24]; var D0 = Rol(C1, 1) ^ C4; var D1 = Rol(C2, 1) ^ C0; var D2 = Rol(C3, 1) ^ C1; var D3 = Rol(C4, 1) ^ C2; var D4 = Rol(C0, 1) ^ C3; state[0] ^= D0; state[5] ^= D0; state[10] ^= D0; state[15] ^= D0; state[20] ^= D0; state[1] ^= D1; state[6] ^= D1; state[11] ^= D1; state[16] ^= D1; state[21] ^= D1; state[2] ^= D2; state[7] ^= D2; state[12] ^= D2; state[17] ^= D2; state[22] ^= D2; state[3] ^= D3; state[8] ^= D3; state[13] ^= D3; state[18] ^= D3; state[23] ^= D3; state[4] ^= D4; state[9] ^= D4; state[14] ^= D4; state[19] ^= D4; state[24] ^= D4; } private static void RhoPi(ulong[] state) { var a = Rol(state[1], 1); state[1] = Rol(state[6], 44); state[6] = Rol(state[9], 20); state[9] = Rol(state[22], 61); state[22] = Rol(state[14], 39); state[14] = Rol(state[20], 18); state[20] = Rol(state[2], 62); state[2] = Rol(state[12], 43); state[12] = Rol(state[13], 25); state[13] = Rol(state[19], 8); state[19] = Rol(state[23], 56); state[23] = Rol(state[15], 41); state[15] = Rol(state[4], 27); state[4] = Rol(state[24], 14); state[24] = Rol(state[21], 2); state[21] = Rol(state[8], 55); state[8] = Rol(state[16], 45); state[16] = Rol(state[5], 36); state[5] = Rol(state[3], 28); state[3] = Rol(state[18], 21); state[18] = Rol(state[17], 15); state[17] = Rol(state[11], 10); state[11] = Rol(state[7], 6); state[7] = Rol(state[10], 3); state[10] = a; } private void Iota(int round, ulong[] state) { state[0] ^= _roundConstants[round]; } private static void Chi(ref ulong C0, ulong[] state, ref ulong C1, ref ulong C2, ref ulong C3, ref ulong C4) { for (var index = 0; index < 25; index += 5) { C0 = state[index] ^ (~state[1 + index] & state[2 + index]); C1 = state[1 + index] ^ (~state[2 + index] & state[3 + index]); C2 = state[2 + index] ^ (~state[3 + index] & state[4 + index]); C3 = state[3 + index] ^ (~state[4 + index] & state[index]); C4 = state[4 + index] ^ (~state[index] & state[1 + index]); state[index] = C0; state[1 + index] = C1; state[2 + index] = C2; state[3 + index] = C3; state[4 + index] = C4; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong Rol(ulong x, byte y) { return (x << y) | (x >> (64 - y)); } }