SHA256HashProvider
namespace SshNet.Security.Cryptography
{
internal class SHA256HashProvider : HashProviderBase
{
private const int DigestSize = 32;
private uint _h1;
private uint _h2;
private uint _h3;
private uint _h4;
private uint _h5;
private uint _h6;
private uint _h7;
private uint _h8;
private readonly uint[] _x;
private int _offset;
private readonly byte[] _buffer;
private int _bufferOffset;
private long _byteCount;
private static readonly uint[] K = new uint[64] {
1116352408,
1899447441,
3049323471,
3921009573,
961987163,
1508970993,
2453635748,
2870763221,
3624381080,
310598401,
607225278,
1426881987,
1925078388,
2162078206,
2614888103,
3248222580,
3835390401,
4022224774,
264347078,
604807628,
770255983,
1249150122,
1555081692,
1996064986,
2554220882,
2821834349,
2952996808,
3210313671,
3336571891,
3584528711,
113926993,
338241895,
666307205,
773529912,
1294757372,
1396182291,
1695183700,
1986661051,
2177026350,
2456956037,
2730485921,
2820302411,
3259730800,
3345764771,
3516065817,
3600352804,
4094571909,
275423344,
430227734,
506948616,
659060556,
883997877,
958139571,
1322822218,
1537002063,
1747873779,
1955562222,
2024104815,
2227730452,
2361852424,
2428436474,
2756734187,
3204031479,
3329325298
};
public override int HashSize => 256;
public override int InputBlockSize => 64;
public override int OutputBlockSize => 64;
public SHA256HashProvider()
{
_buffer = new byte[4];
_x = new uint[64];
InitializeHashValue();
}
public override void HashCore(byte[] array, int ibStart, int cbSize)
{
while (_bufferOffset != 0 && cbSize > 0) {
Update(array[ibStart]);
ibStart++;
cbSize--;
}
while (cbSize > _buffer.Length) {
ProcessWord(array, ibStart);
ibStart += _buffer.Length;
cbSize -= _buffer.Length;
_byteCount += _buffer.Length;
}
while (cbSize > 0) {
Update(array[ibStart]);
ibStart++;
cbSize--;
}
}
public override byte[] HashFinal()
{
byte[] array = new byte[32];
long num = _byteCount << 3;
Update(128);
while (_bufferOffset != 0) {
Update(0);
}
if (_offset > 14)
ProcessBlock();
_x[14] = (uint)((ulong)num >> 32);
_x[15] = (uint)num;
ProcessBlock();
UInt32_To_BE(_h1, array, 0);
UInt32_To_BE(_h2, array, 4);
UInt32_To_BE(_h3, array, 8);
UInt32_To_BE(_h4, array, 12);
UInt32_To_BE(_h5, array, 16);
UInt32_To_BE(_h6, array, 20);
UInt32_To_BE(_h7, array, 24);
UInt32_To_BE(_h8, array, 28);
return array;
}
public override void Reset()
{
InitializeHashValue();
_byteCount = 0;
_bufferOffset = 0;
for (int i = 0; i < _buffer.Length; i++) {
_buffer[i] = 0;
}
_offset = 0;
for (int j = 0; j < _x.Length; j++) {
_x[j] = 0;
}
}
private void InitializeHashValue()
{
_h1 = 1779033703;
_h2 = 3144134277;
_h3 = 1013904242;
_h4 = 2773480762;
_h5 = 1359893119;
_h6 = 2600822924;
_h7 = 528734635;
_h8 = 1541459225;
}
private void Update(byte input)
{
_buffer[_bufferOffset++] = input;
if (_bufferOffset == _buffer.Length) {
ProcessWord(_buffer, 0);
_bufferOffset = 0;
}
_byteCount++;
}
private static uint BE_To_UInt32(byte[] bs, int off)
{
return (uint)((bs[off] << 24) | (bs[++off] << 16) | (bs[++off] << 8) | bs[++off]);
}
private static void UInt32_To_BE(uint n, byte[] bs, int off)
{
bs[off] = (byte)(n >> 24);
bs[++off] = (byte)(n >> 16);
bs[++off] = (byte)(n >> 8);
bs[++off] = (byte)n;
}
private void ProcessWord(byte[] input, int inOff)
{
_x[_offset] = BE_To_UInt32(input, inOff);
if (++_offset == 16)
ProcessBlock();
}
private void ProcessBlock()
{
for (int i = 16; i <= 63; i++) {
_x[i] = Theta1(_x[i - 2]) + _x[i - 7] + Theta0(_x[i - 15]) + _x[i - 16];
}
uint num = _h1;
uint num2 = _h2;
uint num3 = _h3;
uint num4 = _h4;
uint num5 = _h5;
uint num6 = _h6;
uint num7 = _h7;
uint num8 = _h8;
int num9 = 0;
for (int j = 0; j < 8; j++) {
num8 += Sum1Ch(num5, num6, num7) + K[num9] + _x[num9];
num4 += num8;
num8 += Sum0Maj(num, num2, num3);
num9++;
num7 += Sum1Ch(num4, num5, num6) + K[num9] + _x[num9];
num3 += num7;
num7 += Sum0Maj(num8, num, num2);
num9++;
num6 += Sum1Ch(num3, num4, num5) + K[num9] + _x[num9];
num2 += num6;
num6 += Sum0Maj(num7, num8, num);
num9++;
num5 += Sum1Ch(num2, num3, num4) + K[num9] + _x[num9];
num += num5;
num5 += Sum0Maj(num6, num7, num8);
num9++;
num4 += Sum1Ch(num, num2, num3) + K[num9] + _x[num9];
num8 += num4;
num4 += Sum0Maj(num5, num6, num7);
num9++;
num3 += Sum1Ch(num8, num, num2) + K[num9] + _x[num9];
num7 += num3;
num3 += Sum0Maj(num4, num5, num6);
num9++;
num2 += Sum1Ch(num7, num8, num) + K[num9] + _x[num9];
num6 += num2;
num2 += Sum0Maj(num3, num4, num5);
num9++;
num += Sum1Ch(num6, num7, num8) + K[num9] + _x[num9];
num5 += num;
num += Sum0Maj(num2, num3, num4);
num9++;
}
_h1 += num;
_h2 += num2;
_h3 += num3;
_h4 += num4;
_h5 += num5;
_h6 += num6;
_h7 += num7;
_h8 += num8;
_offset = 0;
for (int k = 0; k < _x.Length; k++) {
_x[k] = 0;
}
}
private static uint Sum1Ch(uint x, uint y, uint z)
{
return (((x >> 6) | (x << 26)) ^ ((x >> 11) | (x << 21)) ^ ((x >> 25) | (x << 7))) + ((x & y) ^ (~x & z));
}
private static uint Sum0Maj(uint x, uint y, uint z)
{
return (((x >> 2) | (x << 30)) ^ ((x >> 13) | (x << 19)) ^ ((x >> 22) | (x << 10))) + ((x & y) ^ (x & z) ^ (y & z));
}
private static uint Theta0(uint x)
{
return ((x >> 7) | (x << 25)) ^ ((x >> 18) | (x << 14)) ^ (x >> 3);
}
private static uint Theta1(uint x)
{
return ((x >> 17) | (x << 15)) ^ ((x >> 19) | (x << 13)) ^ (x >> 10);
}
}
}