MD4 Hash Algorithm in C#

The MD4 hash algorithm is a member of RSA's family of hashing recipes. However, due to certain security design flaws it is no longer used in modern cryptography. Surprisingly, it still plays a vital part in Microsoft Windows NTLM authentication and is in widespread use across millions of corporate networks each day.

The following implementation relies heavily on LINQ to make the code both easier to read and to reduce the amount byte array manipulation that often results from this type of coding problem.

using System;
using System.Linq;
using System.Collections.Generic;
using System.Security.Cryptography;

public class MD4 : HashAlgorithm
{
   private uint _a;
   private uint _b;
   private uint _c;
   private uint _d;
   private uint[] _x;
   private int _bytesProcessed;

   public MD4()
   {
      _x = new uint[16];

      Initialize();
   }

   public override void Initialize()
   {
      _a = 0x67452301;
      _b = 0xefcdab89;
      _c = 0x98badcfe;
      _d = 0x10325476;

      _bytesProcessed = 0;
   }

   protected override void HashCore(byte[] array, int offset, int length)
   {
      ProcessMessage(Bytes(array, offset, length));
   }

   protected override byte[] HashFinal()
   {
      try
      {
         ProcessMessage(Padding());

         return new [] {_a, _b, _c, _d}.SelectMany(word => Bytes(word)).ToArray();
      }
      finally
      {
         Initialize();
      }
   }

   private void ProcessMessage(IEnumerable<byte> bytes)
   {
      foreach (byte b in bytes)
      {
         int c = _bytesProcessed & 63;
         int i = c >> 2;
         int s = (c & 3) << 3;

         _x[i] = (_x[i] & ~((uint)255 << s)) | ((uint)b << s);

         if (c == 63)
         {
            Process16WordBlock();
         }

         _bytesProcessed++;
      }
   }

   private static IEnumerable<byte> Bytes(byte[] bytes, int offset, int length)
   {
      for (int i = offset; i < length; i++)
      {
         yield return bytes[i];
      }
   }

   private IEnumerable<byte> Bytes(uint word)
   {
      yield return (byte)(word & 255);
      yield return (byte)((word >> 8) & 255);
      yield return (byte)((word >> 16) & 255);
      yield return (byte)((word >> 24) & 255);
   }

   private IEnumerable<byte> Repeat(byte value, int count)
   {
      for (int i = 0; i < count; i++)
      {
         yield return value;
      }
   }

   private IEnumerable<byte> Padding()
   {
      return Repeat(128, 1)
         .Concat(Repeat(0, ((_bytesProcessed + 8) & 0x7fffffc0) + 55 - _bytesProcessed))
         .Concat(Bytes((uint)_bytesProcessed << 3))
         .Concat(Repeat(0, 4));
   }

   private void Process16WordBlock()
   {
      uint aa = _a;
      uint bb = _b;
      uint cc = _c;
      uint dd = _d;

      foreach (int k in new [] { 0, 4, 8, 12 })
      {
         aa = Round1Operation(aa, bb, cc, dd, _x[k], 3);
         dd = Round1Operation(dd, aa, bb, cc, _x[k + 1], 7);
         cc = Round1Operation(cc, dd, aa, bb, _x[k + 2], 11);
         bb = Round1Operation(bb, cc, dd, aa, _x[k + 3], 19);
      }

      foreach (int k in new [] { 0, 1, 2, 3 })
      {
         aa = Round2Operation(aa, bb, cc, dd, _x[k], 3);
         dd = Round2Operation(dd, aa, bb, cc, _x[k + 4], 5);
         cc = Round2Operation(cc, dd, aa, bb, _x[k + 8], 9);
         bb = Round2Operation(bb, cc, dd, aa, _x[k + 12], 13);
      }

      foreach (int k in new [] { 0, 2, 1, 3 })
      {
         aa = Round3Operation(aa, bb, cc, dd, _x[k], 3);
         dd = Round3Operation(dd, aa, bb, cc, _x[k + 8], 9);
         cc = Round3Operation(cc, dd, aa, bb, _x[k + 4], 11);
         bb = Round3Operation(bb, cc, dd, aa, _x[k + 12], 15);
      }

      unchecked
      {
         _a += aa;
         _b += bb;
         _c += cc;
         _d += dd;
      }
   }

   private static uint ROL(uint value, int numberOfBits)
   {
      return (value << numberOfBits) | (value >> (32 - numberOfBits));
   }

   private static uint Round1Operation(uint a, uint b, uint c, uint d, uint xk, int s)
   {
      unchecked
      {
         return ROL(a + ((b & c) | (~b & d)) + xk, s);
      }
   }

   private static uint Round2Operation(uint a, uint b, uint c, uint d, uint xk, int s)
   {
      unchecked
      {
         return ROL(a + ((b & c) | (b & d) | (c & d)) + xk + 0x5a827999, s);
      }
   }

   private static uint Round3Operation(uint a, uint b, uint c, uint d, uint xk, int s)
   {
      unchecked
      {
         return ROL(a + (b ^ c ^ d) + xk + 0x6ed9eba1, s);
      }
   }
}

To use:

private static byte[] ComputeMD4(byte[] buffer)
{
   using (HashAlgorithm hash = new MD4())
   {
      return hash.ComputeHash(buffer);
   }
}