Skip to content

Instantly share code, notes, and snippets.

@ameerkat
Created October 29, 2016 08:35
Show Gist options
  • Save ameerkat/07a748c9b571289711ebaf61f4b596e9 to your computer and use it in GitHub Desktop.
Save ameerkat/07a748c9b571289711ebaf61f4b596e9 to your computer and use it in GitHub Desktop.
MD5 Implementation in C# based on Wikipedia psuedocode
using System;
using System.Linq;
namespace MD5
{
/// <summary>
/// RFC for MD5 https://tools.ietf.org/html/rfc1321
/// Based on the pseudo code from Wikipedia: https://en.wikipedia.org/wiki/MD5
/// </summary>
public class MD5
{
/*
* Round shift values
*/
static int[] s = new int[64] {
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
};
/*
* Constant K Values
*/
static uint[] K = new uint[64] {
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
};
public static uint leftRotate(uint x, int c)
{
return (x << c) | (x >> (32 - c));
}
// assumes whole bytes as input
public static string Calculate(byte[] input)
{
uint a0 = 0x67452301; // A
uint b0 = 0xefcdab89; // B
uint c0 = 0x98badcfe; // C
uint d0 = 0x10325476; // D
var addLength = (56 - ((input.Length + 1) % 64)) % 64; // calculate the new length with padding
var processedInput = new byte[input.Length + 1 + addLength + 8];
Array.Copy(input, processedInput, input.Length);
processedInput[input.Length] = 0x80; // add 1
byte[] length = BitConverter.GetBytes(input.Length * 8); // bit converter returns little-endian
Array.Copy(length, 0, processedInput, processedInput.Length - 8, 4); // add length in bits
for (int i = 0; i < processedInput.Length / 64; ++i)
{
// copy the input to M
uint[] M = new uint[16];
for(int j = 0; j < 16; ++j)
M[j] = BitConverter.ToUInt32(processedInput, (i * 64) + (j * 4));
// initialize round variables
uint A = a0, B = b0, C = c0, D = d0, F = 0, g = 0;
// primary loop
for (uint k = 0; k < 64; ++k)
{
if (k <= 15)
{
F = (B & C) | (~B & D);
g = k;
}
else if (k >= 16 && k <= 31)
{
F = (D & B) | (~D & C);
g = ((5 * k) + 1) % 16;
}
else if (k >= 32 && k <= 47)
{
F = B ^ C ^ D;
g = ((3 * k) + 5) % 16;
}
else if (k >= 48)
{
F = C ^ (B | ~D);
g = (7 * k) % 16;
}
var dtemp = D;
D = C;
C = B;
B = B + leftRotate((A + F + K[k] + M[g]), s[k]);
A = dtemp;
}
a0 += A;
b0 += B;
c0 += C;
d0 += D;
}
return GetByteString(a0) + GetByteString(b0) + GetByteString(c0) + GetByteString(d0);
}
private static string GetByteString(uint x)
{
return String.Join("", BitConverter.GetBytes(x).Select(y => y.ToString("x2")));
}
}
}
@fperreaultnv
Copy link

... it has a bug that will prevent it from generating the correct MD5 values for certain inputs.

Inputs where (input.Length + 1) % 64 equals between 57 and 63 are problematic indeed, but I believe you can simply replace line 57 with

var addLength = (56 + 64 - ((input.Length + 1) % 64)) % 64; // calculate the new length with padding

Adding the + 64 ensures that we always get a positive number, for (input.Length + 1) % 64 could well be greater than 56, resulting in a negative number, and failing the preprocessing algorithm.

If you think it's clearer, you can also do it like this.

if (addLength < 0) 
{
  addLength += 64;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment