SHA1HashProvider
Implements SHA-1.
using System;
namespace SshNet.Security.Cryptography
{
internal class SHA1HashProvider : HashProviderBase
{
private const int DigestSize = 20;
private const int BlockSize = 64;
private const uint Y1 = 1518500249;
private const uint Y2 = 1859775393;
private const uint Y3 = 2400959708;
private const uint Y4 = 3395469782;
private uint _h1;
private uint _h2;
private uint _h3;
private uint _h4;
private uint _h5;
private readonly uint[] _words;
private readonly byte[] _buffer;
private int _bufferByteCount;
private long _messageByteCount;
public override int HashSize => 160;
public override int InputBlockSize => 64;
public override int OutputBlockSize => 64;
public SHA1HashProvider()
{
_buffer = new byte[64];
_words = new uint[80];
InitializeHashValue();
}
public override void HashCore(byte[] array, int ibStart, int cbSize)
{
_messageByteCount += cbSize;
if (_bufferByteCount > 0 && cbSize + _bufferByteCount >= 64) {
int num = 64 - _bufferByteCount;
Buffer.BlockCopy(array, ibStart, _buffer, _bufferByteCount, num);
ProcessBlock(_buffer, 0);
ibStart += num;
cbSize -= num;
_bufferByteCount = 0;
}
while (cbSize >= 64) {
ProcessBlock(array, ibStart);
ibStart += 64;
cbSize -= 64;
}
if (cbSize > 0) {
Buffer.BlockCopy(array, ibStart, _buffer, _bufferByteCount, cbSize);
_bufferByteCount += cbSize;
}
}
public override byte[] HashFinal()
{
byte[] array = new byte[20];
long number = _messageByteCount << 3;
long num = 64 - _messageByteCount % 64;
if (num <= 8)
num += 64;
byte[] array2 = new byte[num];
array2[0] = 128;
UInt64ToBigEndian((ulong)number, array2, array2.Length - 8);
HashCore(array2, 0, array2.Length);
UInt32ToBigEndian(_h1, array, 0);
UInt32ToBigEndian(_h2, array, 4);
UInt32ToBigEndian(_h3, array, 8);
UInt32ToBigEndian(_h4, array, 12);
UInt32ToBigEndian(_h5, array, 16);
return array;
}
public override void Reset()
{
InitializeHashValue();
_messageByteCount = 0;
_bufferByteCount = 0;
for (int i = 0; i < _buffer.Length; i++) {
_buffer[i] = 0;
}
for (int j = 0; j != _words.Length; j++) {
_words[j] = 0;
}
}
private void InitializeHashValue()
{
_h1 = 1732584193;
_h2 = 4023233417;
_h3 = 2562383102;
_h4 = 271733878;
_h5 = 3285377520;
}
private static uint F(uint u, uint v, uint w)
{
return (u & v) | (~u & w);
}
private static uint H(uint u, uint v, uint w)
{
return u ^ v ^ w;
}
private static uint G(uint u, uint v, uint w)
{
return (u & v) | (u & w) | (v & w);
}
private void ProcessBlock(byte[] buffer, int offset)
{
for (int i = 0; i < 16; i++) {
_words[i] = BigEndianToUInt32(buffer, offset);
offset += 4;
}
for (int j = 16; j < 80; j++) {
uint num = _words[j - 3] ^ _words[j - 8] ^ _words[j - 14] ^ _words[j - 16];
_words[j] = ((num << 1) | (num >> 31));
}
uint num2 = _h1;
uint num3 = _h2;
uint num4 = _h3;
uint num5 = _h4;
uint num6 = _h5;
int num7 = 0;
for (int k = 0; k < 4; k++) {
num6 += ((num2 << 5) | (num2 >> 27)) + F(num3, num4, num5) + _words[num7++] + 1518500249;
num3 = ((num3 << 30) | (num3 >> 2));
num5 += ((num6 << 5) | (num6 >> 27)) + F(num2, num3, num4) + _words[num7++] + 1518500249;
num2 = ((num2 << 30) | (num2 >> 2));
num4 += ((num5 << 5) | (num5 >> 27)) + F(num6, num2, num3) + _words[num7++] + 1518500249;
num6 = ((num6 << 30) | (num6 >> 2));
num3 += ((num4 << 5) | (num4 >> 27)) + F(num5, num6, num2) + _words[num7++] + 1518500249;
num5 = ((num5 << 30) | (num5 >> 2));
num2 += ((num3 << 5) | (num3 >> 27)) + F(num4, num5, num6) + _words[num7++] + 1518500249;
num4 = ((num4 << 30) | (num4 >> 2));
}
for (int l = 0; l < 4; l++) {
num6 += ((num2 << 5) | (num2 >> 27)) + H(num3, num4, num5) + _words[num7++] + 1859775393;
num3 = ((num3 << 30) | (num3 >> 2));
num5 += ((num6 << 5) | (num6 >> 27)) + H(num2, num3, num4) + _words[num7++] + 1859775393;
num2 = ((num2 << 30) | (num2 >> 2));
num4 += ((num5 << 5) | (num5 >> 27)) + H(num6, num2, num3) + _words[num7++] + 1859775393;
num6 = ((num6 << 30) | (num6 >> 2));
num3 += ((num4 << 5) | (num4 >> 27)) + H(num5, num6, num2) + _words[num7++] + 1859775393;
num5 = ((num5 << 30) | (num5 >> 2));
num2 += ((num3 << 5) | (num3 >> 27)) + H(num4, num5, num6) + _words[num7++] + 1859775393;
num4 = ((num4 << 30) | (num4 >> 2));
}
for (int m = 0; m < 4; m++) {
num6 = (uint)((int)num6 + ((int)(((num2 << 5) | (num2 >> 27)) + G(num3, num4, num5) + _words[num7++]) + -1894007588));
num3 = ((num3 << 30) | (num3 >> 2));
num5 = (uint)((int)num5 + ((int)(((num6 << 5) | (num6 >> 27)) + G(num2, num3, num4) + _words[num7++]) + -1894007588));
num2 = ((num2 << 30) | (num2 >> 2));
num4 = (uint)((int)num4 + ((int)(((num5 << 5) | (num5 >> 27)) + G(num6, num2, num3) + _words[num7++]) + -1894007588));
num6 = ((num6 << 30) | (num6 >> 2));
num3 = (uint)((int)num3 + ((int)(((num4 << 5) | (num4 >> 27)) + G(num5, num6, num2) + _words[num7++]) + -1894007588));
num5 = ((num5 << 30) | (num5 >> 2));
num2 = (uint)((int)num2 + ((int)(((num3 << 5) | (num3 >> 27)) + G(num4, num5, num6) + _words[num7++]) + -1894007588));
num4 = ((num4 << 30) | (num4 >> 2));
}
for (int n = 0; n < 4; n++) {
num6 = (uint)((int)num6 + ((int)(((num2 << 5) | (num2 >> 27)) + H(num3, num4, num5) + _words[num7++]) + -899497514));
num3 = ((num3 << 30) | (num3 >> 2));
num5 = (uint)((int)num5 + ((int)(((num6 << 5) | (num6 >> 27)) + H(num2, num3, num4) + _words[num7++]) + -899497514));
num2 = ((num2 << 30) | (num2 >> 2));
num4 = (uint)((int)num4 + ((int)(((num5 << 5) | (num5 >> 27)) + H(num6, num2, num3) + _words[num7++]) + -899497514));
num6 = ((num6 << 30) | (num6 >> 2));
num3 = (uint)((int)num3 + ((int)(((num4 << 5) | (num4 >> 27)) + H(num5, num6, num2) + _words[num7++]) + -899497514));
num5 = ((num5 << 30) | (num5 >> 2));
num2 = (uint)((int)num2 + ((int)(((num3 << 5) | (num3 >> 27)) + H(num4, num5, num6) + _words[num7++]) + -899497514));
num4 = ((num4 << 30) | (num4 >> 2));
}
_h1 += num2;
_h2 += num3;
_h3 += num4;
_h4 += num5;
_h5 += num6;
}
private static uint BigEndianToUInt32(byte[] bs, int off)
{
return (uint)((bs[off] << 24) | (bs[++off] << 16) | (bs[++off] << 8) | bs[++off]);
}
private static void UInt32ToBigEndian(uint number, byte[] buffer, int offset)
{
buffer[offset] = (byte)(number >> 24);
buffer[offset + 1] = (byte)(number >> 16);
buffer[offset + 2] = (byte)(number >> 8);
buffer[offset + 3] = (byte)number;
}
private static void UInt64ToBigEndian(ulong number, byte[] buffer, int offset)
{
UInt32ToBigEndian((uint)(number >> 32), buffer, offset);
UInt32ToBigEndian((uint)number, buffer, offset + 4);
}
}
}