From ffc6bc045d4f65f2e97f6c119974adb8124ea2ae Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sat, 9 Nov 2024 02:54:04 -0500 Subject: [PATCH] Add RIPEMD-320 implementation --- README.MD | 2 +- SabreTools.Hashing.Test/TestHelper.cs | 1 + SabreTools.Hashing/HashType.cs | 5 + SabreTools.Hashing/HashWrapper.cs | 9 + SabreTools.Hashing/RipeMD/Constants.cs | 13 +- SabreTools.Hashing/RipeMD/RipeMD128.cs | 3 +- SabreTools.Hashing/RipeMD/RipeMD160.cs | 3 +- SabreTools.Hashing/RipeMD/RipeMD320.cs | 765 +++++++++++++++++++++++++ 8 files changed, 796 insertions(+), 5 deletions(-) create mode 100644 SabreTools.Hashing/RipeMD/RipeMD320.cs 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