KeccakSpongeManaged.cs

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));
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *