diff --git a/README.MD b/README.MD
index 8bf38e6..9262422 100644
--- a/README.MD
+++ b/README.MD
@@ -4,7 +4,7 @@ This library comprises of methods and helpers to simplify the process of getting
| Source | Hash / Checksum Types | Notes |
| --- | --- | --- |
-| \<**INTERNAL**\> | Standardized CRC implementations, RIPEMD-128, RIPEMD-160, xxHash-32, xxHash-64 | Built for compatability, written from original specifications where possible |
+| \<**INTERNAL**\> | Standardized CRC implementations, RIPEMD-128, RIPEMD-160, RIPEMD-320, xxHash-32, xxHash-64 | Built for compatability, written from original specifications where possible |
| [Aaru.Checksums](https://github.com/aaru-dps/Aaru.Checksums) | Adler-32, Fletcher-16, Fletcher-32, SpamSum | Some code tweaks made to support older .NET versions |
| [Blake3.NET](https://github.com/xoofx/Blake3.NET) | BLAKE3 | Used in `net7.0` and above |
| [System.IO.Hashing](https://www.nuget.org/packages/System.IO.Hashing) | XXH3, XXH128 | Used in `net462` and above |
diff --git a/SabreTools.Hashing.Test/TestHelper.cs b/SabreTools.Hashing.Test/TestHelper.cs
index c613b05..fadfe56 100644
--- a/SabreTools.Hashing.Test/TestHelper.cs
+++ b/SabreTools.Hashing.Test/TestHelper.cs
@@ -168,6 +168,7 @@ namespace SabreTools.Hashing.Test
{HashType.RIPEMD128, "6356cc18225245de3ca9afcb4fa22ce6"},
{HashType.RIPEMD160, "346361e1d7fdb836650cecdb842b0dbe660eed66"},
+ {HashType.RIPEMD320, "a523bec87b0738f89d8ae5cf0edd3ee9c7b9811f1051e32893e32e820db33841b9d5042e738d20c9"},
{HashType.SHA1, "eea1ee2d801d830c4bdad4df3c8da6f9f52d1a9f"},
{HashType.SHA256, "fdb02dee8c319c52087382c45f099c90d0b6cc824850aff28c1bfb2884b7b855"},
diff --git a/SabreTools.Hashing/HashType.cs b/SabreTools.Hashing/HashType.cs
index 63148de..9e38b77 100644
--- a/SabreTools.Hashing/HashType.cs
+++ b/SabreTools.Hashing/HashType.cs
@@ -730,6 +730,11 @@ namespace SabreTools.Hashing
///
RIPEMD160,
+ ///
+ /// RIPEMD-320 hash
+ ///
+ RIPEMD320,
+
#endregion
#region SHA
diff --git a/SabreTools.Hashing/HashWrapper.cs b/SabreTools.Hashing/HashWrapper.cs
index eb6a21e..a284be3 100644
--- a/SabreTools.Hashing/HashWrapper.cs
+++ b/SabreTools.Hashing/HashWrapper.cs
@@ -59,6 +59,8 @@ namespace SabreTools.Hashing
return r128.GetHash();
case RipeMD.RipeMD160 r160:
return r160.GetHash();
+ case RipeMD.RipeMD320 r320:
+ return r320.GetHash();
#if NET8_0_OR_GREATER
case Shake128 s128:
@@ -289,6 +291,7 @@ namespace SabreTools.Hashing
HashType.RIPEMD128 => new RipeMD.RipeMD128(),
HashType.RIPEMD160 => new RipeMD.RipeMD160(),
+ HashType.RIPEMD320 => new RipeMD.RipeMD320(),
HashType.SHA1 => SHA1.Create(),
HashType.SHA256 => SHA256.Create(),
@@ -359,6 +362,9 @@ namespace SabreTools.Hashing
case RipeMD.RipeMD160 r160:
r160.TransformBlock(buffer, offset, size);
break;
+ case RipeMD.RipeMD320 r320:
+ r320.TransformBlock(buffer, offset, size);
+ break;
#if NET8_0_OR_GREATER
case Shake128 s128:
@@ -400,6 +406,9 @@ namespace SabreTools.Hashing
case RipeMD.RipeMD160 r160:
r160.Terminate();
break;
+ case RipeMD.RipeMD320 r320:
+ r320.Terminate();
+ break;
}
}
diff --git a/SabreTools.Hashing/RipeMD/Constants.cs b/SabreTools.Hashing/RipeMD/Constants.cs
index 5a2b263..493df53 100644
--- a/SabreTools.Hashing/RipeMD/Constants.cs
+++ b/SabreTools.Hashing/RipeMD/Constants.cs
@@ -3,7 +3,7 @@ namespace SabreTools.Hashing.RipeMD
//
internal static class Constants
{
- #region RIPEMD-128
+ #region RIPEMD-128 / RIPEMD-256
public const uint RMD128Round00To15 = 0x00000000;
public const uint RMD128Round16To31 = 0x5A827999;
@@ -19,10 +19,14 @@ namespace SabreTools.Hashing.RipeMD
public const uint RMD128Y1 = 0xEFCDAB89;
public const uint RMD128Y2 = 0x98BADCFE;
public const uint RMD128Y3 = 0x10325476;
+ public const uint RMD256Y4 = 0x76543210;
+ public const uint RMD256Y5 = 0xFEDCBA98;
+ public const uint RMD256Y6 = 0x89ABCDEF;
+ public const uint RMD256Y7 = 0x01234567;
#endregion
- #region RIPEMD-160
+ #region RIPEMD-160 / RIPEMD-320
public const uint RMD160Round00To15 = 0x00000000;
public const uint RMD160Round16To31 = 0x5A827999;
@@ -41,6 +45,11 @@ namespace SabreTools.Hashing.RipeMD
public const uint RMD160Y2 = 0x98BADCFE;
public const uint RMD160Y3 = 0x10325476;
public const uint RMD160Y4 = 0xC3D2E1F0;
+ public const uint RMD320Y5 = 0x76543210;
+ public const uint RMD320Y6 = 0xFEDCBA98;
+ public const uint RMD320Y7 = 0x89ABCDEF;
+ public const uint RMD320Y8 = 0x01234567;
+ public const uint RMD320Y9 = 0x3C2D1E0F;
///
/// t_i
diff --git a/SabreTools.Hashing/RipeMD/RipeMD128.cs b/SabreTools.Hashing/RipeMD/RipeMD128.cs
index 1783509..154329c 100644
--- a/SabreTools.Hashing/RipeMD/RipeMD128.cs
+++ b/SabreTools.Hashing/RipeMD/RipeMD128.cs
@@ -3,7 +3,8 @@ using static SabreTools.Hashing.RipeMD.Constants;
namespace SabreTools.Hashing.RipeMD
{
- ///
+ ///
+ ///
internal class RipeMD128
{
///
diff --git a/SabreTools.Hashing/RipeMD/RipeMD160.cs b/SabreTools.Hashing/RipeMD/RipeMD160.cs
index 1a4d813..be8f224 100644
--- a/SabreTools.Hashing/RipeMD/RipeMD160.cs
+++ b/SabreTools.Hashing/RipeMD/RipeMD160.cs
@@ -3,7 +3,8 @@ using static SabreTools.Hashing.RipeMD.Constants;
namespace SabreTools.Hashing.RipeMD
{
- ///
+ ///
+ ///
internal class RipeMD160
{
///
diff --git a/SabreTools.Hashing/RipeMD/RipeMD320.cs b/SabreTools.Hashing/RipeMD/RipeMD320.cs
new file mode 100644
index 0000000..0bc422d
--- /dev/null
+++ b/SabreTools.Hashing/RipeMD/RipeMD320.cs
@@ -0,0 +1,765 @@
+using System;
+using static SabreTools.Hashing.RipeMD.Constants;
+
+namespace SabreTools.Hashing.RipeMD
+{
+ ///
+ ///
+ internal class RipeMD320
+ {
+ ///
+ /// Set of 10 32-bit numbers representing the hash state
+ ///
+ private readonly uint[] _state = new uint[10];
+
+ ///
+ /// Total number of bytes processed
+ ///
+ private long _totalBytes;
+
+ ///
+ /// Internal byte buffer to accumulate before
+ ///
+ private readonly byte[] _buffer = new byte[64];
+
+ ///
+ /// Internal UInt32 buffer for processing
+ ///
+ private readonly uint[] _block = new uint[16];
+
+ public RipeMD320()
+ {
+ Reset();
+ }
+
+ ///
+ /// Reset the internal hashing state
+ ///
+ public void Reset()
+ {
+ // Reset the seed values
+ _state[0] = RMD160Y0;
+ _state[1] = RMD160Y1;
+ _state[2] = RMD160Y2;
+ _state[3] = RMD160Y3;
+ _state[4] = RMD160Y4;
+ _state[5] = RMD320Y5;
+ _state[6] = RMD320Y6;
+ _state[7] = RMD320Y7;
+ _state[8] = RMD320Y8;
+ _state[9] = RMD320Y9;
+
+ // Reset the byte count
+ _totalBytes = 0;
+
+ // Reset the buffers
+ Array.Clear(_buffer, 0, _buffer.Length);
+ Array.Clear(_block, 0, _block.Length);
+ }
+
+ ///
+ /// Hash a block of data and append it to the existing hash
+ ///
+ /// Byte array representing the data
+ /// Offset in the byte array to include
+ /// Length of the data to hash
+ public void TransformBlock(byte[] data, int offset, int length)
+ {
+ // Figure out how much buffer is needed
+ int bufferLen = (int)(_totalBytes & 0x3f);
+
+ // Increment the processed byte count
+ _totalBytes += length;
+
+ // If there is buffer to fill and it will meet the limit
+ if (bufferLen > 0 && bufferLen + length >= 64)
+ {
+ // Fill the buffer from the input
+ Array.Copy(data, offset, _buffer, bufferLen, 64 - bufferLen);
+
+ // Set the new values
+ offset += 64 - bufferLen;
+ length -= 64 - bufferLen;
+
+ // Split the buffer for the round
+ for (int i = 0; i < 16; i++)
+ {
+ _block[i] = ReadLE32(_buffer, i * 4);
+ }
+
+ // Run the round
+ Round();
+ bufferLen = 0;
+ }
+
+ /// Process any standalone blocks
+ while (length >= 64)
+ {
+ // Fill the buffer from the input
+ Array.Copy(data, offset, _buffer, 0, 64);
+
+ // Set the new values
+ offset += 64;
+ length -= 64;
+
+ // Split the buffer for the round
+ for (int i = 0; i < 16; i++)
+ {
+ _block[i] = ReadLE32(_buffer, i * 4);
+ }
+
+ // Run the round
+ Round();
+ }
+
+ // Save the remainder in the buffer
+ if (length > 0)
+ Array.Copy(data, offset, _buffer, bufferLen, length);
+ }
+
+ ///
+ /// End the hashing process
+ ///
+ public void Terminate()
+ {
+ // Determine the pad length
+ int padLength = 64 - (int)(_totalBytes & 0x3f);
+ if (padLength <= 8)
+ padLength += 64;
+
+ // Get the total byte count in bits
+ long totalBitCount = _totalBytes * 8;
+
+ // Prebuild the padding
+ var padding = new byte[padLength];
+ padding[0] = 0x80;
+ padding[padLength - 1] = (byte)((totalBitCount >> 56) & 0xff);
+ padding[padLength - 2] = (byte)((totalBitCount >> 48) & 0xff);
+ padding[padLength - 3] = (byte)((totalBitCount >> 40) & 0xff);
+ padding[padLength - 4] = (byte)((totalBitCount >> 32) & 0xff);
+ padding[padLength - 5] = (byte)((totalBitCount >> 24) & 0xff);
+ padding[padLength - 6] = (byte)((totalBitCount >> 16) & 0xff);
+ padding[padLength - 7] = (byte)((totalBitCount >> 8 ) & 0xff);
+ padding[padLength - 8] = (byte)((totalBitCount >> 0 ) & 0xff);
+
+ // Pad the block
+ TransformBlock(padding, 0, padding.Length);
+ }
+
+ ///
+ /// Get the current value of the hash
+ ///
+ ///
+ /// If has not been run, this value
+ /// will not be accurate for the processed bytes so far.
+ ///
+ public byte[] GetHash()
+ {
+ var hash = new byte[40];
+ int hashOffset = 0;
+
+ // Assemble the hash array
+ for (int i = 0; i < _state.Length; i++)
+ {
+ byte[] segment = BitConverter.GetBytes(_state[i]);
+ Array.Copy(segment, 0, hash, hashOffset, 4);
+ hashOffset += 4;
+ }
+
+ // Reset the state and return
+ Reset();
+ return hash;
+ }
+
+ ///
+ /// Perform one round of updates on the cached values
+ ///
+ ///
+ /// The official specification for RIPEMD-160 includes tables
+ /// and instructions that represent a loop. Most standard implementations
+ /// use the unrolled version of that loop to make it more efficient.
+ ///
+ /// The below code started with the looped version but has been converted
+ /// to the more standard implementation instead.
+ ///
+ private void Round()
+ {
+ // Setup values
+ uint x0 = _state[0], xp0 = _state[5];
+ uint x1 = _state[1], xp1 = _state[6];
+ uint x2 = _state[2], xp2 = _state[7];
+ uint x3 = _state[3], xp3 = _state[8];
+ uint x4 = _state[4], xp4 = _state[9];
+ uint t;
+
+ #region Rounds 0-15
+
+ // Round 0
+ x0 = RotateLeft(x0 + G00_15(x1, x2, x3) + _block[0] + RMD160Round00To15, 11) + x4;
+ x2 = RotateLeft(x2, 10);
+ xp0 = RotateLeft(xp0 + G64_79(xp1, xp2, xp3) + _block[5] + RMD160RoundPrime00To15, 8) + xp4;
+ xp2 = RotateLeft(xp2, 10);
+
+ // Round 1
+ x4 = RotateLeft(x4 + G00_15(x0, x1, x2) + _block[1] + RMD160Round00To15, 14) + x3;
+ x1 = RotateLeft(x1, 10);
+ xp4 = RotateLeft(xp4 + G64_79(xp0, xp1, xp2) + _block[14] + RMD160RoundPrime00To15, 9) + xp3;
+ xp1 = RotateLeft(xp1, 10);
+
+ // Round 2
+ x3 = RotateLeft(x3 + G00_15(x4, x0, x1) + _block[2] + RMD160Round00To15, 15) + x2;
+ x0 = RotateLeft(x0, 10);
+ xp3 = RotateLeft(xp3 + G64_79(xp4, xp0, xp1) + _block[7] + RMD160RoundPrime00To15, 9) + xp2;
+ xp0 = RotateLeft(xp0, 10);
+
+ // Round 3
+ x2 = RotateLeft(x2 + G00_15(x3, x4, x0) + _block[3] + RMD160Round00To15, 12) + x1;
+ x4 = RotateLeft(x4, 10);
+ xp2 = RotateLeft(xp2 + G64_79(xp3, xp4, xp0) + _block[0] + RMD160RoundPrime00To15, 11) + xp1;
+ xp4 = RotateLeft(xp4, 10);
+
+ // Round 4
+ x1 = RotateLeft(x1 + G00_15(x2, x3, x4) + _block[4] + RMD160Round00To15, 5) + x0;
+ x3 = RotateLeft(x3, 10);
+ xp1 = RotateLeft(xp1 + G64_79(xp2, xp3, xp4) + _block[9] + RMD160RoundPrime00To15, 13) + xp0;
+ xp3 = RotateLeft(xp3, 10);
+
+ // Round 5
+ x0 = RotateLeft(x0 + G00_15(x1, x2, x3) + _block[5] + RMD160Round00To15, 8) + x4;
+ x2 = RotateLeft(x2, 10);
+ xp0 = RotateLeft(xp0 + G64_79(xp1, xp2, xp3) + _block[2] + RMD160RoundPrime00To15, 15) + xp4;
+ xp2 = RotateLeft(xp2, 10);
+
+ // Round 6
+ x4 = RotateLeft(x4 + G00_15(x0, x1, x2) + _block[6] + RMD160Round00To15, 7) + x3;
+ x1 = RotateLeft(x1, 10);
+ xp4 = RotateLeft(xp4 + G64_79(xp0, xp1, xp2) + _block[11] + RMD160RoundPrime00To15, 15) + xp3;
+ xp1 = RotateLeft(xp1, 10);
+
+ // Round 7
+ x3 = RotateLeft(x3 + G00_15(x4, x0, x1) + _block[7] + RMD160Round00To15, 9) + x2;
+ x0 = RotateLeft(x0, 10);
+ xp3 = RotateLeft(xp3 + G64_79(xp4, xp0, xp1) + _block[4] + RMD160RoundPrime00To15, 5) + xp2;
+ xp0 = RotateLeft(xp0, 10);
+
+ // Round 8
+ x2 = RotateLeft(x2 + G00_15(x3, x4, x0) + _block[8] + RMD160Round00To15, 11) + x1;
+ x4 = RotateLeft(x4, 10);
+ xp2 = RotateLeft(xp2 + G64_79(xp3, xp4, xp0) + _block[13] + RMD160RoundPrime00To15, 7) + xp1;
+ xp4 = RotateLeft(xp4, 10);
+
+ // Round 9
+ x1 = RotateLeft(x1 + G00_15(x2, x3, x4) + _block[9] + RMD160Round00To15, 13) + x0;
+ x3 = RotateLeft(x3, 10);
+ xp1 = RotateLeft(xp1 + G64_79(xp2, xp3, xp4) + _block[6] + RMD160RoundPrime00To15, 7) + xp0;
+ xp3 = RotateLeft(xp3, 10);
+
+ // Round 10
+ x0 = RotateLeft(x0 + G00_15(x1, x2, x3) + _block[10] + RMD160Round00To15, 14) + x4;
+ x2 = RotateLeft(x2, 10);
+ xp0 = RotateLeft(xp0 + G64_79(xp1, xp2, xp3) + _block[15] + RMD160RoundPrime00To15, 8) + xp4;
+ xp2 = RotateLeft(xp2, 10);
+
+ // Round 11
+ x4 = RotateLeft(x4 + G00_15(x0, x1, x2) + _block[11] + RMD160Round00To15, 15) + x3;
+ x1 = RotateLeft(x1, 10);
+ xp4 = RotateLeft(xp4 + G64_79(xp0, xp1, xp2) + _block[8] + RMD160RoundPrime00To15, 11) + xp3;
+ xp1 = RotateLeft(xp1, 10);
+
+ // Round 12
+ x3 = RotateLeft(x3 + G00_15(x4, x0, x1) + _block[12] + RMD160Round00To15, 6) + x2;
+ x0 = RotateLeft(x0, 10);
+ xp3 = RotateLeft(xp3 + G64_79(xp4, xp0, xp1) + _block[1] + RMD160RoundPrime00To15, 14) + xp2;
+ xp0 = RotateLeft(xp0, 10);
+
+ // Round 13
+ x2 = RotateLeft(x2 + G00_15(x3, x4, x0) + _block[13] + RMD160Round00To15, 7) + x1;
+ x4 = RotateLeft(x4, 10);
+ xp2 = RotateLeft(xp2 + G64_79(xp3, xp4, xp0) + _block[10] + RMD160RoundPrime00To15, 14) + xp1;
+ xp4 = RotateLeft(xp4, 10);
+
+ // Round 14
+ x1 = RotateLeft(x1 + G00_15(x2, x3, x4) + _block[14] + RMD160Round00To15, 9) + x0;
+ x3 = RotateLeft(x3, 10);
+ xp1 = RotateLeft(xp1 + G64_79(xp2, xp3, xp4) + _block[3] + RMD160RoundPrime00To15, 12) + xp0;
+ xp3 = RotateLeft(xp3, 10);
+
+ // Round 15
+ x0 = RotateLeft(x0 + G00_15(x1, x2, x3) + _block[15] + RMD160Round00To15, 8) + x4;
+ x2 = RotateLeft(x2, 10);
+ xp0 = RotateLeft(xp0 + G64_79(xp1, xp2, xp3) + _block[12] + RMD160RoundPrime00To15, 6) + xp4;
+ xp2 = RotateLeft(xp2, 10);
+
+ // Swap set 1
+ t = x0; x0 = xp0; xp0 = t;
+
+ #endregion
+
+ #region Rounds 16-31
+
+ // Round 16
+ x4 = RotateLeft(x4 + G16_31(x0, x1, x2) + _block[7] + RMD160Round16To31, 7) + x3;
+ x1 = RotateLeft(x1, 10);
+ xp4 = RotateLeft(xp4 + G48_63(xp0, xp1, xp2) + _block[6] + RMD160RoundPrime16To31, 9) + xp3;
+ xp1 = RotateLeft(xp1, 10);
+
+ // Round 17
+ x3 = RotateLeft(x3 + G16_31(x4, x0, x1) + _block[4] + RMD160Round16To31, 6) + x2;
+ x0 = RotateLeft(x0, 10);
+ xp3 = RotateLeft(xp3 + G48_63(xp4, xp0, xp1) + _block[11] + RMD160RoundPrime16To31, 13) + xp2;
+ xp0 = RotateLeft(xp0, 10);
+
+ // Round 18
+ x2 = RotateLeft(x2 + G16_31(x3, x4, x0) + _block[13] + RMD160Round16To31, 8) + x1;
+ x4 = RotateLeft(x4, 10);
+ xp2 = RotateLeft(xp2 + G48_63(xp3, xp4, xp0) + _block[3] + RMD160RoundPrime16To31, 15) + xp1;
+ xp4 = RotateLeft(xp4, 10);
+
+ // Round 19
+ x1 = RotateLeft(x1 + G16_31(x2, x3, x4) + _block[1] + RMD160Round16To31, 13) + x0;
+ x3 = RotateLeft(x3, 10);
+ xp1 = RotateLeft(xp1 + G48_63(xp2, xp3, xp4) + _block[7] + RMD160RoundPrime16To31, 7) + xp0;
+ xp3 = RotateLeft(xp3, 10);
+
+ // Round 20
+ x0 = RotateLeft(x0 + G16_31(x1, x2, x3) + _block[10] + RMD160Round16To31, 11) + x4;
+ x2 = RotateLeft(x2, 10);
+ xp0 = RotateLeft(xp0 + G48_63(xp1, xp2, xp3) + _block[0] + RMD160RoundPrime16To31, 12) + xp4;
+ xp2 = RotateLeft(xp2, 10);
+
+ // Round 21
+ x4 = RotateLeft(x4 + G16_31(x0, x1, x2) + _block[6] + RMD160Round16To31, 9) + x3;
+ x1 = RotateLeft(x1, 10);
+ xp4 = RotateLeft(xp4 + G48_63(xp0, xp1, xp2) + _block[13] + RMD160RoundPrime16To31, 8) + xp3;
+ xp1 = RotateLeft(xp1, 10);
+
+ // Round 22
+ x3 = RotateLeft(x3 + G16_31(x4, x0, x1) + _block[15] + RMD160Round16To31, 7) + x2;
+ x0 = RotateLeft(x0, 10);
+ xp3 = RotateLeft(xp3 + G48_63(xp4, xp0, xp1) + _block[5] + RMD160RoundPrime16To31, 9) + xp2;
+ xp0 = RotateLeft(xp0, 10);
+
+ // Round 23
+ x2 = RotateLeft(x2 + G16_31(x3, x4, x0) + _block[3] + RMD160Round16To31, 15) + x1;
+ x4 = RotateLeft(x4, 10);
+ xp2 = RotateLeft(xp2 + G48_63(xp3, xp4, xp0) + _block[10] + RMD160RoundPrime16To31, 11) + xp1;
+ xp4 = RotateLeft(xp4, 10);
+
+ // Round 24
+ x1 = RotateLeft(x1 + G16_31(x2, x3, x4) + _block[12] + RMD160Round16To31, 7) + x0;
+ x3 = RotateLeft(x3, 10);
+ xp1 = RotateLeft(xp1 + G48_63(xp2, xp3, xp4) + _block[14] + RMD160RoundPrime16To31, 7) + xp0;
+ xp3 = RotateLeft(xp3, 10);
+
+ // Round 25
+ x0 = RotateLeft(x0 + G16_31(x1, x2, x3) + _block[0] + RMD160Round16To31, 12) + x4;
+ x2 = RotateLeft(x2, 10);
+ xp0 = RotateLeft(xp0 + G48_63(xp1, xp2, xp3) + _block[15] + RMD160RoundPrime16To31, 7) + xp4;
+ xp2 = RotateLeft(xp2, 10);
+
+ // Round 26
+ x4 = RotateLeft(x4 + G16_31(x0, x1, x2) + _block[9] + RMD160Round16To31, 15) + x3;
+ x1 = RotateLeft(x1, 10);
+ xp4 = RotateLeft(xp4 + G48_63(xp0, xp1, xp2) + _block[8] + RMD160RoundPrime16To31, 12) + xp3;
+ xp1 = RotateLeft(xp1, 10);
+
+ // Round 27
+ x3 = RotateLeft(x3 + G16_31(x4, x0, x1) + _block[5] + RMD160Round16To31, 9) + x2;
+ x0 = RotateLeft(x0, 10);
+ xp3 = RotateLeft(xp3 + G48_63(xp4, xp0, xp1) + _block[12] + RMD160RoundPrime16To31, 7) + xp2;
+ xp0 = RotateLeft(xp0, 10);
+
+ // Round 28
+ x2 = RotateLeft(x2 + G16_31(x3, x4, x0) + _block[2] + RMD160Round16To31, 11) + x1;
+ x4 = RotateLeft(x4, 10);
+ xp2 = RotateLeft(xp2 + G48_63(xp3, xp4, xp0) + _block[4] + RMD160RoundPrime16To31, 6) + xp1;
+ xp4 = RotateLeft(xp4, 10);
+
+ // Round 29
+ x1 = RotateLeft(x1 + G16_31(x2, x3, x4) + _block[14] + RMD160Round16To31, 7) + x0;
+ x3 = RotateLeft(x3, 10);
+ xp1 = RotateLeft(xp1 + G48_63(xp2, xp3, xp4) + _block[9] + RMD160RoundPrime16To31, 15) + xp0;
+ xp3 = RotateLeft(xp3, 10);
+
+ // Round 30
+ x0 = RotateLeft(x0 + G16_31(x1, x2, x3) + _block[11] + RMD160Round16To31, 13) + x4;
+ x2 = RotateLeft(x2, 10);
+ xp0 = RotateLeft(xp0 + G48_63(xp1, xp2, xp3) + _block[1] + RMD160RoundPrime16To31, 13) + xp4;
+ xp2 = RotateLeft(xp2, 10);
+
+ // Round 31
+ x4 = RotateLeft(x4 + G16_31(x0, x1, x2) + _block[8] + RMD160Round16To31, 12) + x3;
+ x1 = RotateLeft(x1, 10);
+ xp4 = RotateLeft(xp4 + G48_63(xp0, xp1, xp2) + _block[2] + RMD160RoundPrime16To31, 11) + xp3;
+ xp1 = RotateLeft(xp1, 10);
+
+ // Swap set 2
+ t = x1; x1 = xp1; xp1 = t;
+
+ #endregion
+
+ #region Rounds 32-47
+
+ // Round 32
+ x3 = RotateLeft(x3 + G32_47(x4, x0, x1) + _block[3] + RMD160Round32To47, 11) + x2;
+ x0 = RotateLeft(x0, 10);
+ xp3 = RotateLeft(xp3 + G32_47(xp4, xp0, xp1) + _block[15] + RMD160RoundPrime32To47, 9) + xp2;
+ xp0 = RotateLeft(xp0, 10);
+
+ // Round 33
+ x2 = RotateLeft(x2 + G32_47(x3, x4, x0) + _block[10] + RMD160Round32To47, 13) + x1;
+ x4 = RotateLeft(x4, 10);
+ xp2 = RotateLeft(xp2 + G32_47(xp3, xp4, xp0) + _block[5] + RMD160RoundPrime32To47, 7) + xp1;
+ xp4 = RotateLeft(xp4, 10);
+
+ // Round 34
+ x1 = RotateLeft(x1 + G32_47(x2, x3, x4) + _block[14] + RMD160Round32To47, 6) + x0;
+ x3 = RotateLeft(x3, 10);
+ xp1 = RotateLeft(xp1 + G32_47(xp2, xp3, xp4) + _block[1] + RMD160RoundPrime32To47, 15) + xp0;
+ xp3 = RotateLeft(xp3, 10);
+
+ // Round 35
+ x0 = RotateLeft(x0 + G32_47(x1, x2, x3) + _block[4] + RMD160Round32To47, 7) + x4;
+ x2 = RotateLeft(x2, 10);
+ xp0 = RotateLeft(xp0 + G32_47(xp1, xp2, xp3) + _block[3] + RMD160RoundPrime32To47, 11) + xp4;
+ xp2 = RotateLeft(xp2, 10);
+
+ // Round 36
+ x4 = RotateLeft(x4 + G32_47(x0, x1, x2) + _block[9] + RMD160Round32To47, 14) + x3;
+ x1 = RotateLeft(x1, 10);
+ xp4 = RotateLeft(xp4 + G32_47(xp0, xp1, xp2) + _block[7] + RMD160RoundPrime32To47, 8) + xp3;
+ xp1 = RotateLeft(xp1, 10);
+
+ // Round 37
+ x3 = RotateLeft(x3 + G32_47(x4, x0, x1) + _block[15] + RMD160Round32To47, 9) + x2;
+ x0 = RotateLeft(x0, 10);
+ xp3 = RotateLeft(xp3 + G32_47(xp4, xp0, xp1) + _block[14] + RMD160RoundPrime32To47, 6) + xp2;
+ xp0 = RotateLeft(xp0, 10);
+
+ // Round 38
+ x2 = RotateLeft(x2 + G32_47(x3, x4, x0) + _block[8] + RMD160Round32To47, 13) + x1;
+ x4 = RotateLeft(x4, 10);
+ xp2 = RotateLeft(xp2 + G32_47(xp3, xp4, xp0) + _block[6] + RMD160RoundPrime32To47, 6) + xp1;
+ xp4 = RotateLeft(xp4, 10);
+
+ // Round 39
+ x1 = RotateLeft(x1 + G32_47(x2, x3, x4) + _block[1] + RMD160Round32To47, 15) + x0;
+ x3 = RotateLeft(x3, 10);
+ xp1 = RotateLeft(xp1 + G32_47(xp2, xp3, xp4) + _block[9] + RMD160RoundPrime32To47, 14) + xp0;
+ xp3 = RotateLeft(xp3, 10);
+
+ // Round 40
+ x0 = RotateLeft(x0 + G32_47(x1, x2, x3) + _block[2] + RMD160Round32To47, 14) + x4;
+ x2 = RotateLeft(x2, 10);
+ xp0 = RotateLeft(xp0 + G32_47(xp1, xp2, xp3) + _block[11] + RMD160RoundPrime32To47, 12) + xp4;
+ xp2 = RotateLeft(xp2, 10);
+
+ // Round 41
+ x4 = RotateLeft(x4 + G32_47(x0, x1, x2) + _block[7] + RMD160Round32To47, 8) + x3;
+ x1 = RotateLeft(x1, 10);
+ xp4 = RotateLeft(xp4 + G32_47(xp0, xp1, xp2) + _block[8] + RMD160RoundPrime32To47, 13) + xp3;
+ xp1 = RotateLeft(xp1, 10);
+
+ // Round 42
+ x3 = RotateLeft(x3 + G32_47(x4, x0, x1) + _block[0] + RMD160Round32To47, 13) + x2;
+ x0 = RotateLeft(x0, 10);
+ xp3 = RotateLeft(xp3 + G32_47(xp4, xp0, xp1) + _block[12] + RMD160RoundPrime32To47, 5) + xp2;
+ xp0 = RotateLeft(xp0, 10);
+
+ // Round 43
+ x2 = RotateLeft(x2 + G32_47(x3, x4, x0) + _block[6] + RMD160Round32To47, 6) + x1;
+ x4 = RotateLeft(x4, 10);
+ xp2 = RotateLeft(xp2 + G32_47(xp3, xp4, xp0) + _block[2] + RMD160RoundPrime32To47, 14) + xp1;
+ xp4 = RotateLeft(xp4, 10);
+
+ // Round 44
+ x1 = RotateLeft(x1 + G32_47(x2, x3, x4) + _block[13] + RMD160Round32To47, 5) + x0;
+ x3 = RotateLeft(x3, 10);
+ xp1 = RotateLeft(xp1 + G32_47(xp2, xp3, xp4) + _block[10] + RMD160RoundPrime32To47, 13) + xp0;
+ xp3 = RotateLeft(xp3, 10);
+
+ // Round 45
+ x0 = RotateLeft(x0 + G32_47(x1, x2, x3) + _block[11] + RMD160Round32To47, 12) + x4;
+ x2 = RotateLeft(x2, 10);
+ xp0 = RotateLeft(xp0 + G32_47(xp1, xp2, xp3) + _block[0] + RMD160RoundPrime32To47, 13) + xp4;
+ xp2 = RotateLeft(xp2, 10);
+
+ // Round 46
+ x4 = RotateLeft(x4 + G32_47(x0, x1, x2) + _block[5] + RMD160Round32To47, 7) + x3;
+ x1 = RotateLeft(x1, 10);
+ xp4 = RotateLeft(xp4 + G32_47(xp0, xp1, xp2) + _block[4] + RMD160RoundPrime32To47, 7) + xp3;
+ xp1 = RotateLeft(xp1, 10);
+
+ // Round 47
+ x3 = RotateLeft(x3 + G32_47(x4, x0, x1) + _block[12] + RMD160Round32To47, 5) + x2;
+ x0 = RotateLeft(x0, 10);
+ xp3 = RotateLeft(xp3 + G32_47(xp4, xp0, xp1) + _block[13] + RMD160RoundPrime32To47, 5) + xp2;
+ xp0 = RotateLeft(xp0, 10);
+
+ // Swap set 3
+ t = x2; x2 = xp2; xp2 = t;
+
+ #endregion
+
+ #region Rounds 48-63
+
+ // Round 48
+ x2 = RotateLeft(x2 + G48_63(x3, x4, x0) + _block[1] + RMD160Round48To63, 11) + x1;
+ x4 = RotateLeft(x4, 10);
+ xp2 = RotateLeft(xp2 + G16_31(xp3, xp4, xp0) + _block[8] + RMD160RoundPrime48To63, 15) + xp1;
+ xp4 = RotateLeft(xp4, 10);
+
+ // Round 49
+ x1 = RotateLeft(x1 + G48_63(x2, x3, x4) + _block[9] + RMD160Round48To63, 12) + x0;
+ x3 = RotateLeft(x3, 10);
+ xp1 = RotateLeft(xp1 + G16_31(xp2, xp3, xp4) + _block[6] + RMD160RoundPrime48To63, 5) + xp0;
+ xp3 = RotateLeft(xp3, 10);
+
+ // Round 50
+ x0 = RotateLeft(x0 + G48_63(x1, x2, x3) + _block[11] + RMD160Round48To63, 14) + x4;
+ x2 = RotateLeft(x2, 10);
+ xp0 = RotateLeft(xp0 + G16_31(xp1, xp2, xp3) + _block[4] + RMD160RoundPrime48To63, 8) + xp4;
+ xp2 = RotateLeft(xp2, 10);
+
+ // Round 51
+ x4 = RotateLeft(x4 + G48_63(x0, x1, x2) + _block[10] + RMD160Round48To63, 15) + x3;
+ x1 = RotateLeft(x1, 10);
+ xp4 = RotateLeft(xp4 + G16_31(xp0, xp1, xp2) + _block[1] + RMD160RoundPrime48To63, 11) + xp3;
+ xp1 = RotateLeft(xp1, 10);
+
+ // Round 52
+ x3 = RotateLeft(x3 + G48_63(x4, x0, x1) + _block[0] + RMD160Round48To63, 14) + x2;
+ x0 = RotateLeft(x0, 10);
+ xp3 = RotateLeft(xp3 + G16_31(xp4, xp0, xp1) + _block[3] + RMD160RoundPrime48To63, 14) + xp2;
+ xp0 = RotateLeft(xp0, 10);
+
+ // Round 53
+ x2 = RotateLeft(x2 + G48_63(x3, x4, x0) + _block[8] + RMD160Round48To63, 15) + x1;
+ x4 = RotateLeft(x4, 10);
+ xp2 = RotateLeft(xp2 + G16_31(xp3, xp4, xp0) + _block[11] + RMD160RoundPrime48To63, 14) + xp1;
+ xp4 = RotateLeft(xp4, 10);
+
+ // Round 54
+ x1 = RotateLeft(x1 + G48_63(x2, x3, x4) + _block[12] + RMD160Round48To63, 9) + x0;
+ x3 = RotateLeft(x3, 10);
+ xp1 = RotateLeft(xp1 + G16_31(xp2, xp3, xp4) + _block[15] + RMD160RoundPrime48To63, 6) + xp0;
+ xp3 = RotateLeft(xp3, 10);
+
+ // Round 55
+ x0 = RotateLeft(x0 + G48_63(x1, x2, x3) + _block[4] + RMD160Round48To63, 8) + x4;
+ x2 = RotateLeft(x2, 10);
+ xp0 = RotateLeft(xp0 + G16_31(xp1, xp2, xp3) + _block[0] + RMD160RoundPrime48To63, 14) + xp4;
+ xp2 = RotateLeft(xp2, 10);
+
+ // Round 56
+ x4 = RotateLeft(x4 + G48_63(x0, x1, x2) + _block[13] + RMD160Round48To63, 9) + x3;
+ x1 = RotateLeft(x1, 10);
+ xp4 = RotateLeft(xp4 + G16_31(xp0, xp1, xp2) + _block[5] + RMD160RoundPrime48To63, 6) + xp3;
+ xp1 = RotateLeft(xp1, 10);
+
+ // Round 57
+ x3 = RotateLeft(x3 + G48_63(x4, x0, x1) + _block[3] + RMD160Round48To63, 14) + x2;
+ x0 = RotateLeft(x0, 10);
+ xp3 = RotateLeft(xp3 + G16_31(xp4, xp0, xp1) + _block[12] + RMD160RoundPrime48To63, 9) + xp2;
+ xp0 = RotateLeft(xp0, 10);
+
+ // Round 58
+ x2 = RotateLeft(x2 + G48_63(x3, x4, x0) + _block[7] + RMD160Round48To63, 5) + x1;
+ x4 = RotateLeft(x4, 10);
+ xp2 = RotateLeft(xp2 + G16_31(xp3, xp4, xp0) + _block[2] + RMD160RoundPrime48To63, 12) + xp1;
+ xp4 = RotateLeft(xp4, 10);
+
+ // Round 59
+ x1 = RotateLeft(x1 + G48_63(x2, x3, x4) + _block[15] + RMD160Round48To63, 6) + x0;
+ x3 = RotateLeft(x3, 10);
+ xp1 = RotateLeft(xp1 + G16_31(xp2, xp3, xp4) + _block[13] + RMD160RoundPrime48To63, 9) + xp0;
+ xp3 = RotateLeft(xp3, 10);
+
+ // Round 60
+ x0 = RotateLeft(x0 + G48_63(x1, x2, x3) + _block[14] + RMD160Round48To63, 8) + x4;
+ x2 = RotateLeft(x2, 10);
+ xp0 = RotateLeft(xp0 + G16_31(xp1, xp2, xp3) + _block[9] + RMD160RoundPrime48To63, 12) + xp4;
+ xp2 = RotateLeft(xp2, 10);
+
+ // Round 61
+ x4 = RotateLeft(x4 + G48_63(x0, x1, x2) + _block[5] + RMD160Round48To63, 6) + x3;
+ x1 = RotateLeft(x1, 10);
+ xp4 = RotateLeft(xp4 + G16_31(xp0, xp1, xp2) + _block[7] + RMD160RoundPrime48To63, 5) + xp3;
+ xp1 = RotateLeft(xp1, 10);
+
+ // Round 62
+ x3 = RotateLeft(x3 + G48_63(x4, x0, x1) + _block[6] + RMD160Round48To63, 5) + x2;
+ x0 = RotateLeft(x0, 10);
+ xp3 = RotateLeft(xp3 + G16_31(xp4, xp0, xp1) + _block[10] + RMD160RoundPrime48To63, 15) + xp2;
+ xp0 = RotateLeft(xp0, 10);
+
+ // Round 63
+ x2 = RotateLeft(x2 + G48_63(x3, x4, x0) + _block[2] + RMD160Round48To63, 12) + x1;
+ x4 = RotateLeft(x4, 10);
+ xp2 = RotateLeft(xp2 + G16_31(xp3, xp4, xp0) + _block[14] + RMD160RoundPrime48To63, 8) + xp1;
+ xp4 = RotateLeft(xp4, 10);
+
+ // Swap set 4
+ t = x3; x3 = xp3; xp3 = t;
+
+ #endregion
+
+ #region Rounds 64-79
+
+ // Round 64
+ x1 = RotateLeft(x1 + G64_79(x2, x3, x4) + _block[4] + RMD160Round64To79, 9) + x0;
+ x3 = RotateLeft(x3, 10);
+ xp1 = RotateLeft(xp1 + G00_15(xp2, xp3, xp4) + _block[12] + RMD160RoundPrime64To79, 8) + xp0;
+ xp3 = RotateLeft(xp3, 10);
+
+ // Round 65
+ x0 = RotateLeft(x0 + G64_79(x1, x2, x3) + _block[0] + RMD160Round64To79, 15) + x4;
+ x2 = RotateLeft(x2, 10);
+ xp0 = RotateLeft(xp0 + G00_15(xp1, xp2, xp3) + _block[15] + RMD160RoundPrime64To79, 5) + xp4;
+ xp2 = RotateLeft(xp2, 10);
+
+ // Round 66
+ x4 = RotateLeft(x4 + G64_79(x0, x1, x2) + _block[5] + RMD160Round64To79, 5) + x3;
+ x1 = RotateLeft(x1, 10);
+ xp4 = RotateLeft(xp4 + G00_15(xp0, xp1, xp2) + _block[10] + RMD160RoundPrime64To79, 12) + xp3;
+ xp1 = RotateLeft(xp1, 10);
+
+ // Round 67
+ x3 = RotateLeft(x3 + G64_79(x4, x0, x1) + _block[9] + RMD160Round64To79, 11) + x2;
+ x0 = RotateLeft(x0, 10);
+ xp3 = RotateLeft(xp3 + G00_15(xp4, xp0, xp1) + _block[4] + RMD160RoundPrime64To79, 9) + xp2;
+ xp0 = RotateLeft(xp0, 10);
+
+ // Round 68
+ x2 = RotateLeft(x2 + G64_79(x3, x4, x0) + _block[7] + RMD160Round64To79, 6) + x1;
+ x4 = RotateLeft(x4, 10);
+ xp2 = RotateLeft(xp2 + G00_15(xp3, xp4, xp0) + _block[1] + RMD160RoundPrime64To79, 12) + xp1;
+ xp4 = RotateLeft(xp4, 10);
+
+ // Round 69
+ x1 = RotateLeft(x1 + G64_79(x2, x3, x4) + _block[12] + RMD160Round64To79, 8) + x0;
+ x3 = RotateLeft(x3, 10);
+ xp1 = RotateLeft(xp1 + G00_15(xp2, xp3, xp4) + _block[5] + RMD160RoundPrime64To79, 5) + xp0;
+ xp3 = RotateLeft(xp3, 10);
+
+ // Round 70
+ x0 = RotateLeft(x0 + G64_79(x1, x2, x3) + _block[2] + RMD160Round64To79, 13) + x4;
+ x2 = RotateLeft(x2, 10);
+ xp0 = RotateLeft(xp0 + G00_15(xp1, xp2, xp3) + _block[8] + RMD160RoundPrime64To79, 14) + xp4;
+ xp2 = RotateLeft(xp2, 10);
+
+ // Round 71
+ x4 = RotateLeft(x4 + G64_79(x0, x1, x2) + _block[10] + RMD160Round64To79, 12) + x3;
+ x1 = RotateLeft(x1, 10);
+ xp4 = RotateLeft(xp4 + G00_15(xp0, xp1, xp2) + _block[7] + RMD160RoundPrime64To79, 6) + xp3;
+ xp1 = RotateLeft(xp1, 10);
+
+ // Round 72
+ x3 = RotateLeft(x3 + G64_79(x4, x0, x1) + _block[14] + RMD160Round64To79, 5) + x2;
+ x0 = RotateLeft(x0, 10);
+ xp3 = RotateLeft(xp3 + G00_15(xp4, xp0, xp1) + _block[6] + RMD160RoundPrime64To79, 8) + xp2;
+ xp0 = RotateLeft(xp0, 10);
+
+ // Round 73
+ x2 = RotateLeft(x2 + G64_79(x3, x4, x0) + _block[1] + RMD160Round64To79, 12) + x1;
+ x4 = RotateLeft(x4, 10);
+ xp2 = RotateLeft(xp2 + G00_15(xp3, xp4, xp0) + _block[2] + RMD160RoundPrime64To79, 13) + xp1;
+ xp4 = RotateLeft(xp4, 10);
+
+ // Round 74
+ x1 = RotateLeft(x1 + G64_79(x2, x3, x4) + _block[3] + RMD160Round64To79, 13) + x0;
+ x3 = RotateLeft(x3, 10);
+ xp1 = RotateLeft(xp1 + G00_15(xp2, xp3, xp4) + _block[13] + RMD160RoundPrime64To79, 6) + xp0;
+ xp3 = RotateLeft(xp3, 10);
+
+ // Round 75
+ x0 = RotateLeft(x0 + G64_79(x1, x2, x3) + _block[8] + RMD160Round64To79, 14) + x4;
+ x2 = RotateLeft(x2, 10);
+ xp0 = RotateLeft(xp0 + G00_15(xp1, xp2, xp3) + _block[14] + RMD160RoundPrime64To79, 5) + xp4;
+ xp2 = RotateLeft(xp2, 10);
+
+ // Round 76
+ x4 = RotateLeft(x4 + G64_79(x0, x1, x2) + _block[11] + RMD160Round64To79, 11) + x3;
+ x1 = RotateLeft(x1, 10);
+ xp4 = RotateLeft(xp4 + G00_15(xp0, xp1, xp2) + _block[0] + RMD160RoundPrime64To79, 15) + xp3;
+ xp1 = RotateLeft(xp1, 10);
+
+ // Round 77
+ x3 = RotateLeft(x3 + G64_79(x4, x0, x1) + _block[6] + RMD160Round64To79, 8) + x2;
+ x0 = RotateLeft(x0, 10);
+ xp3 = RotateLeft(xp3 + G00_15(xp4, xp0, xp1) + _block[3] + RMD160RoundPrime64To79, 13) + xp2;
+ xp0 = RotateLeft(xp0, 10);
+
+ // Round 78
+ x2 = RotateLeft(x2 + G64_79(x3, x4, x0) + _block[15] + RMD160Round64To79, 5) + x1;
+ x4 = RotateLeft(x4, 10);
+ xp2 = RotateLeft(xp2 + G00_15(xp3, xp4, xp0) + _block[9] + RMD160RoundPrime64To79, 11) + xp1;
+ xp4 = RotateLeft(xp4, 10);
+
+ // Round 79
+ x1 = RotateLeft(x1 + G64_79(x2, x3, x4) + _block[13] + RMD160Round64To79, 6) + x0;
+ x3 = RotateLeft(x3, 10);
+ xp1 = RotateLeft(xp1 + G00_15(xp2, xp3, xp4) + _block[11] + RMD160RoundPrime64To79, 11) + xp0;
+ xp3 = RotateLeft(xp3, 10);
+
+ // Swap set 5
+ t = x4; x4 = xp4; xp4 = t;
+
+ #endregion
+
+ // Avalanche values
+ _state[0] += x0;
+ _state[1] += x1;
+ _state[2] += x2;
+ _state[3] += x3;
+ _state[4] += x4;
+ _state[5] += xp0;
+ _state[6] += xp1;
+ _state[7] += xp2;
+ _state[8] += xp3;
+ _state[9] += xp4;
+ }
+
+ ///
+ /// Round operation [0, 15]
+ ///
+ private static uint G00_15(uint x, uint y, uint z) => x ^ y ^ z;
+
+ ///
+ /// Round operation [16, 31]
+ ///
+ private static uint G16_31(uint x, uint y, uint z) => (x & y) | (~x & z);
+
+ ///
+ /// Round operation [32, 47]
+ ///
+ private static uint G32_47(uint x, uint y, uint z) => (x | ~y) ^ z;
+
+ ///
+ /// Round operation [48, 63]
+ ///
+ private static uint G48_63(uint x, uint y, uint z) => (x & z) | (y & ~z);
+
+ ///
+ /// Round operation [64, 79]
+ ///
+ private static uint G64_79(uint x, uint y, uint z) => x ^ (y | ~z);
+
+ ///
+ /// 32-bit little-endian read
+ ///
+ public static uint ReadLE32(byte[] data, int offset)
+ {
+ return (uint)(data[offset + 0]
+ | data[offset + 1] << 8
+ | data[offset + 2] << 16
+ | data[offset + 3] << 24);
+ }
+
+ ///
+ /// 32-bit rotate left
+ ///
+ private static uint RotateLeft(uint value, int shift)
+ => (value << shift) | (value >> (32 - shift));
+ }
+}
\ No newline at end of file