|
|
@@ -0,0 +1,382 @@
|
|
|
+using System.Security.Cryptography;
|
|
|
+using System;
|
|
|
+
|
|
|
+namespace Renci.SshNet.Security.Cryptography
|
|
|
+{
|
|
|
+ /// <summary>
|
|
|
+ /// SHA1 algorithm implementation
|
|
|
+ /// </summary>
|
|
|
+ public class SHA1Hash : HashAlgorithm
|
|
|
+ {
|
|
|
+ private const int DIGEST_SIZE = 20;
|
|
|
+
|
|
|
+ private const uint Y1 = 0x5a827999;
|
|
|
+
|
|
|
+ private const uint Y2 = 0x6ed9eba1;
|
|
|
+
|
|
|
+ private const uint Y3 = 0x8f1bbcdc;
|
|
|
+
|
|
|
+ private const uint Y4 = 0xca62c1d6;
|
|
|
+
|
|
|
+ private uint H1, H2, H3, H4, H5;
|
|
|
+
|
|
|
+ private uint[] _hashValue = new uint[80];
|
|
|
+
|
|
|
+ private int _offset;
|
|
|
+
|
|
|
+ private byte[] _buffer;
|
|
|
+
|
|
|
+ private int _bufferOffset;
|
|
|
+
|
|
|
+ private long _byteCount;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the size, in bits, of the computed hash code.
|
|
|
+ /// </summary>
|
|
|
+ /// <returns>The size, in bits, of the computed hash code.</returns>
|
|
|
+ public override int HashSize
|
|
|
+ {
|
|
|
+ get
|
|
|
+ {
|
|
|
+ return DIGEST_SIZE * 8;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the input block size.
|
|
|
+ /// </summary>
|
|
|
+ /// <returns>The input block size.</returns>
|
|
|
+ public override int InputBlockSize
|
|
|
+ {
|
|
|
+ get
|
|
|
+ {
|
|
|
+ return 64;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the output block size.
|
|
|
+ /// </summary>
|
|
|
+ /// <returns>The output block size.</returns>
|
|
|
+ public override int OutputBlockSize
|
|
|
+ {
|
|
|
+ get
|
|
|
+ {
|
|
|
+ return 64;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets a value indicating whether the current transform can be reused.
|
|
|
+ /// </summary>
|
|
|
+ /// <returns>Always true.</returns>
|
|
|
+ public override bool CanReuseTransform
|
|
|
+ {
|
|
|
+ get
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets a value indicating whether multiple blocks can be transformed.
|
|
|
+ /// </summary>
|
|
|
+ /// <returns>true if multiple blocks can be transformed; otherwise, false.</returns>
|
|
|
+ public override bool CanTransformMultipleBlocks
|
|
|
+ {
|
|
|
+ get
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Initializes a new instance of the <see cref="SHA1Hash"/> class.
|
|
|
+ /// </summary>
|
|
|
+ public SHA1Hash()
|
|
|
+ {
|
|
|
+ this._buffer = new byte[4];
|
|
|
+ this.Initialize();
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Routes data written to the object into the hash algorithm for computing the hash.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="array">The input to compute the hash code for.</param>
|
|
|
+ /// <param name="ibStart">The offset into the byte array from which to begin using data.</param>
|
|
|
+ /// <param name="cbSize">The number of bytes in the byte array to use as data.</param>
|
|
|
+ protected override void HashCore(byte[] array, int ibStart, int cbSize)
|
|
|
+ {
|
|
|
+ // Fill the current word
|
|
|
+ while ((this._bufferOffset != 0) && (cbSize > 0))
|
|
|
+ {
|
|
|
+ this.Update(array[ibStart]);
|
|
|
+ ibStart++;
|
|
|
+ cbSize--;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Process whole words.
|
|
|
+ while (cbSize > this._buffer.Length)
|
|
|
+ {
|
|
|
+ this.ProcessWord(array, ibStart);
|
|
|
+
|
|
|
+ ibStart += this._buffer.Length;
|
|
|
+ cbSize -= this._buffer.Length;
|
|
|
+ this._byteCount += this._buffer.Length;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Load in the remainder.
|
|
|
+ while (cbSize > 0)
|
|
|
+ {
|
|
|
+ this.Update(array[ibStart]);
|
|
|
+
|
|
|
+ ibStart++;
|
|
|
+ cbSize--;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Finalizes the hash computation after the last data is processed by the cryptographic stream object.
|
|
|
+ /// </summary>
|
|
|
+ /// <returns>
|
|
|
+ /// The computed hash code.
|
|
|
+ /// </returns>
|
|
|
+ protected override byte[] HashFinal()
|
|
|
+ {
|
|
|
+ var output = new byte[DIGEST_SIZE];
|
|
|
+ long bitLength = (this._byteCount << 3);
|
|
|
+
|
|
|
+ //
|
|
|
+ // add the pad bytes.
|
|
|
+ //
|
|
|
+ this.Update((byte)128);
|
|
|
+
|
|
|
+ while (this._bufferOffset != 0)
|
|
|
+ this.Update((byte)0);
|
|
|
+
|
|
|
+ if (this._offset > 14)
|
|
|
+ {
|
|
|
+ this.ProcessBlock();
|
|
|
+ }
|
|
|
+
|
|
|
+ _hashValue[14] = (uint)((ulong)bitLength >> 32);
|
|
|
+ _hashValue[15] = (uint)((ulong)bitLength);
|
|
|
+
|
|
|
+
|
|
|
+ this.ProcessBlock();
|
|
|
+
|
|
|
+ UInt32_To__BE(H1, output, 0);
|
|
|
+ UInt32_To__BE(H2, output, 0 + 4);
|
|
|
+ UInt32_To__BE(H3, output, 0 + 8);
|
|
|
+ UInt32_To__BE(H4, output, 0 + 12);
|
|
|
+ UInt32_To__BE(H5, output, 0 + 16);
|
|
|
+
|
|
|
+ this.Initialize();
|
|
|
+
|
|
|
+ return output;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Initializes an implementation of the <see cref="T:System.Security.Cryptography.HashAlgorithm"/> class.
|
|
|
+ /// </summary>
|
|
|
+ public override void Initialize()
|
|
|
+ {
|
|
|
+ this._byteCount = 0;
|
|
|
+ this._bufferOffset = 0;
|
|
|
+ Array.Clear(this._buffer, 0, this._buffer.Length);
|
|
|
+
|
|
|
+ H1 = 0x67452301;
|
|
|
+ H2 = 0xefcdab89;
|
|
|
+ H3 = 0x98badcfe;
|
|
|
+ H4 = 0x10325476;
|
|
|
+ H5 = 0xc3d2e1f0;
|
|
|
+
|
|
|
+ this._offset = 0;
|
|
|
+ Array.Clear(_hashValue, 0, _hashValue.Length);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private void Update(byte input)
|
|
|
+ {
|
|
|
+ this._buffer[this._bufferOffset++] = input;
|
|
|
+
|
|
|
+ if (this._bufferOffset == this._buffer.Length)
|
|
|
+ {
|
|
|
+ this.ProcessWord(this._buffer, 0);
|
|
|
+ this._bufferOffset = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ this._byteCount++;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void ProcessWord(byte[] input, int inOff)
|
|
|
+ {
|
|
|
+ _hashValue[this._offset] = BE_To__UInt32(input, inOff);
|
|
|
+
|
|
|
+ if (++this._offset == 16)
|
|
|
+ {
|
|
|
+ this.ProcessBlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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()
|
|
|
+ {
|
|
|
+ //
|
|
|
+ // expand 16 word block into 80 word block.
|
|
|
+ //
|
|
|
+ for (int i = 16; i < 80; i++)
|
|
|
+ {
|
|
|
+ uint t = _hashValue[i - 3] ^ _hashValue[i - 8] ^ _hashValue[i - 14] ^ _hashValue[i - 16];
|
|
|
+ _hashValue[i] = t << 1 | t >> 31;
|
|
|
+ }
|
|
|
+
|
|
|
+ //
|
|
|
+ // set up working variables.
|
|
|
+ //
|
|
|
+ uint A = H1;
|
|
|
+ uint B = H2;
|
|
|
+ uint C = H3;
|
|
|
+ uint D = H4;
|
|
|
+ uint E = H5;
|
|
|
+
|
|
|
+ //
|
|
|
+ // round 1
|
|
|
+ //
|
|
|
+ int idx = 0;
|
|
|
+
|
|
|
+ for (int j = 0; j < 4; j++)
|
|
|
+ {
|
|
|
+ // E = rotateLeft(A, 5) + F(B, C, D) + E + X[idx++] + Y1
|
|
|
+ // B = rotateLeft(B, 30)
|
|
|
+ E += (A << 5 | (A >> 27)) + F(B, C, D) + _hashValue[idx++] + Y1;
|
|
|
+ B = B << 30 | (B >> 2);
|
|
|
+
|
|
|
+ D += (E << 5 | (E >> 27)) + F(A, B, C) + _hashValue[idx++] + Y1;
|
|
|
+ A = A << 30 | (A >> 2);
|
|
|
+
|
|
|
+ C += (D << 5 | (D >> 27)) + F(E, A, B) + _hashValue[idx++] + Y1;
|
|
|
+ E = E << 30 | (E >> 2);
|
|
|
+
|
|
|
+ B += (C << 5 | (C >> 27)) + F(D, E, A) + _hashValue[idx++] + Y1;
|
|
|
+ D = D << 30 | (D >> 2);
|
|
|
+
|
|
|
+ A += (B << 5 | (B >> 27)) + F(C, D, E) + _hashValue[idx++] + Y1;
|
|
|
+ C = C << 30 | (C >> 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ //
|
|
|
+ // round 2
|
|
|
+ //
|
|
|
+ for (int j = 0; j < 4; j++)
|
|
|
+ {
|
|
|
+ // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y2
|
|
|
+ // B = rotateLeft(B, 30)
|
|
|
+ E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y2;
|
|
|
+ B = B << 30 | (B >> 2);
|
|
|
+
|
|
|
+ D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y2;
|
|
|
+ A = A << 30 | (A >> 2);
|
|
|
+
|
|
|
+ C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y2;
|
|
|
+ E = E << 30 | (E >> 2);
|
|
|
+
|
|
|
+ B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y2;
|
|
|
+ D = D << 30 | (D >> 2);
|
|
|
+
|
|
|
+ A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y2;
|
|
|
+ C = C << 30 | (C >> 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ //
|
|
|
+ // round 3
|
|
|
+ //
|
|
|
+ for (int j = 0; j < 4; j++)
|
|
|
+ {
|
|
|
+ // E = rotateLeft(A, 5) + G(B, C, D) + E + X[idx++] + Y3
|
|
|
+ // B = rotateLeft(B, 30)
|
|
|
+ E += (A << 5 | (A >> 27)) + G(B, C, D) + _hashValue[idx++] + Y3;
|
|
|
+ B = B << 30 | (B >> 2);
|
|
|
+
|
|
|
+ D += (E << 5 | (E >> 27)) + G(A, B, C) + _hashValue[idx++] + Y3;
|
|
|
+ A = A << 30 | (A >> 2);
|
|
|
+
|
|
|
+ C += (D << 5 | (D >> 27)) + G(E, A, B) + _hashValue[idx++] + Y3;
|
|
|
+ E = E << 30 | (E >> 2);
|
|
|
+
|
|
|
+ B += (C << 5 | (C >> 27)) + G(D, E, A) + _hashValue[idx++] + Y3;
|
|
|
+ D = D << 30 | (D >> 2);
|
|
|
+
|
|
|
+ A += (B << 5 | (B >> 27)) + G(C, D, E) + _hashValue[idx++] + Y3;
|
|
|
+ C = C << 30 | (C >> 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ //
|
|
|
+ // round 4
|
|
|
+ //
|
|
|
+ for (int j = 0; j < 4; j++)
|
|
|
+ {
|
|
|
+ // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y4
|
|
|
+ // B = rotateLeft(B, 30)
|
|
|
+ E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y4;
|
|
|
+ B = B << 30 | (B >> 2);
|
|
|
+
|
|
|
+ D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y4;
|
|
|
+ A = A << 30 | (A >> 2);
|
|
|
+
|
|
|
+ C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y4;
|
|
|
+ E = E << 30 | (E >> 2);
|
|
|
+
|
|
|
+ B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y4;
|
|
|
+ D = D << 30 | (D >> 2);
|
|
|
+
|
|
|
+ A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y4;
|
|
|
+ C = C << 30 | (C >> 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ H1 += A;
|
|
|
+ H2 += B;
|
|
|
+ H3 += C;
|
|
|
+ H4 += D;
|
|
|
+ H5 += E;
|
|
|
+
|
|
|
+ //
|
|
|
+ // reset start of the buffer.
|
|
|
+ //
|
|
|
+ this._offset = 0;
|
|
|
+ Array.Clear(_hashValue, 0, 16);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static uint BE_To__UInt32(byte[] bs, int off)
|
|
|
+ {
|
|
|
+ uint n = (uint)bs[off] << 24;
|
|
|
+ n |= (uint)bs[++off] << 16;
|
|
|
+ n |= (uint)bs[++off] << 8;
|
|
|
+ n |= (uint)bs[++off];
|
|
|
+ return n;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|