Created
March 24, 2020 21:32
-
-
Save mjs3339/46e9069d6bb5c73106e52a33c2598dc1 to your computer and use it in GitHub Desktop.
SHA3 Implementation in C#
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Runtime.CompilerServices; | |
/// <summary> | |
/// Max size is 768 Bits. Proof: 768 / 8 = 96 * 2 = 192, 200-192=8 | |
/// </summary> | |
[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(); | |
} | |
} | |
/// <summary> | |
/// **** CHECKED VERSION **** | |
/// https://en.wikipedia.org/wiki/SHA-3 | |
/// </summary> | |
[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); | |
RC(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 RC(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)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment